#include "KEPsolver.h"

KEPsolver::KEPsolver() {

}

KEPsolver::KEPsolver(Instance* instance, const string& fileName, const string& IKEPname) {
	this->instance = instance;
	this->fileName = fileName;
	this->IKEPname = IKEPname;
	this->value = 0.0;
	this->time = 0.0;
}

KEPsolver::KEPsolver(const KEPsolver& s) {
	this->instance = s.instance;
	this->fileName = s.fileName;
	this->IKEPname = s.IKEPname;
	this->length_stats = s.length_stats;
	this->csv_line = s.csv_line;
	this->value = s.value;
	this->time = s.time;
	this->solution = s.solution;
}

KEPsolver::~KEPsolver()
{
}


bool operator==(const KEPsolver& s1, const KEPsolver& ss2) {
	return false;
}

bool operator!=(const KEPsolver& s1, const KEPsolver& s2) {
	return !(s1 == s2);
}

KEPsolver& KEPsolver::operator=(const KEPsolver& s) {
	this->instance = s.instance;
	this->fileName = s.fileName;
	this->IKEPname = s.IKEPname;
	this->length_stats = s.length_stats;
	this->csv_line = s.csv_line;
	this->value = s.value;
	this->time = s.time;
	this->solution = s.solution;
	return *this;
}

void KEPsolver::solve(const bool& arePathsPriced, const bool& areCyclesPriced, const double& time_limit) {

	Exchange::restartIds();
	clock_t start = clock();

	/*cout << "---Instance" << endl;
	cout << this->instance->toString() << endl;*/

	RestrictedMasterProblem rmp(*this->instance);
	CyclesInitializer cycles(*this->instance);
	ChainsInitializer chains(*this->instance);
	cycles.set_next_algorithm(&chains);
	rmp.set_intializer(&cycles);
	rmp.set_request("WHATEVER");
	rmp.initialize();
	rmp.construct();

	/*clock_t ff = clock();
	cout << "DUR " << (float)(ff - start) / CLOCKS_PER_SEC << "\t" << rmp.get_columns().size() << "\n";*/

	//We create the pricing problem
	PricingProblem psp(*this->instance);
	psp.set_rmp_dual_variables(&rmp.get_dual_variables());
	psp.set_existing_rmp_columns(&rmp.get_columns());
	psp.set_branching_constraints(&rmp.get_branching_constraints());
	psp.set_cut_constraints(&rmp.get_cut_constraints());

	ExactGeneratorCyclesChains* exact_generator = new ExactGeneratorCyclesChains(*this->instance, psp.get_rmp_dual_variables(), psp.get_existing_rmp_columns(), psp.get_branching_constraints(), psp.get_cut_constraints());
	exact_generator->set_name("EXACTGENERATOR");
	psp.set_algorithm(exact_generator);
	psp.set_request("EXACTGENERATOR");

	//We create the column genration mediator that coordinates the transmission of dual variables and new columns between RMP and PSP
	ColumnGenerationMediator<Instance, vector<double>, DualSolution, vector<Exchange>, GRBConstr> mediator(&rmp, &psp);
	rmp.set_mediator(&mediator);
	psp.set_mediator(&mediator);

	//We create the column generation procedue
	ColumnGeneration<RestrictedMasterProblem, PricingProblem> column_generation(&rmp, &psp);
	//column_generation.run(1500.0);

	//the branching rule (or chain of branching rules if many)
	ArcBranch* arcBranch = new ArcBranch;
	VisitBranch* visitBranch = new VisitBranch;
	ExchangeNumberBranch* exchangeBranch = new ExchangeNumberBranch;
	exchangeBranch->set_next_branching_rule(visitBranch)->set_next_branching_rule(arcBranch);
	//visitBranch->set_next_branching_rule(arcBranch);
	rmp.set_branching_request("WHATEVER");
	//rmp.set_branching_request("NOBRANCHING");

	//the search strategy 
	BestFirstStrategy<RestrictedMasterProblem, PricingProblem, ColumnGeneration<RestrictedMasterProblem,
		PricingProblem>, GRBConstr> best_first_strategy(true);

	//The cutters (chain of responsibility of cut procedures per cut family)
	//CutPool* cut_pool = new CutPool;
	vector<int> multipliers = { 1,1,1 };
	int gcd = 2;
	OddHolePadberg* odd_hole = new OddHolePadberg(this->instance->getVertices(), 20);
	CliqueInequalities* clique_inequalities = new CliqueInequalities(this->instance->getVertices(), 20);
	clique_inequalities->set_next_cutter(odd_hole);
	rmp.set_cut_request("WHATEVER");
	//rmp.set_cut_request("ODDHOLEPADBERG");
	//rmp.set_cut_request("NOCUTS");

	//Branch and price and cut algorithm that we set with RMP, PSP, column generation,
	//branching rules, search strategy, cutters
	BranchAndPrice<RestrictedMasterProblem, PricingProblem, ColumnGeneration<RestrictedMasterProblem,
		PricingProblem>, GRBConstr, vector<Exchange>> bpc(&rmp, &psp, &column_generation, true, true);

	bpc.set_branching_rule(arcBranch);
	bpc.set_search_strategy(&best_first_strategy);
	bpc.set_cut_generator(clique_inequalities);

	//Run the algorithm 
	bpc.run(time_limit, ::OMEGA);
	clock_t end = clock();
	

	this->solution.clear();
	for (const auto& col_id : bpc.get_best_columns_int()) {
		this->solution.push_back(rmp.get_columns().at(col_id));
	}

	this->time = (float)(end - start) / CLOCKS_PER_SEC;
	this->bound = bpc.get_best_bound();
	this->value = bpc.get_best_incumbent_value();
	SolutionStats ss = SolutionStats(this->instance, this->solution);
	this->length_stats = ss.compute_length_stats();

	//Display the best integer solution the algorithm has found
	bpc.display();
	bpc.sanity_check();
	//string path_instance_name = "C:\\Users\\mogier\\Desktop\\PhD\\Code\\KEP_IIKEP\\Checker\\" + name + "_sol.txt";

	if (this->fileName != "") {

		/*for (auto& cut_family_info : clique_inequalities->get_information()) {
			cout << cut_family_info->toString() << "\n";
		}
		for (auto& cut_family_info : odd_hole->get_information()) {
			cout << cut_family_info->toString() << "\n";
		}*/

		//string path_instance_name = ::solutionPath + this->fileName + "_sol.txt";
		string path_solution_name = this->fileName + "_sol_exchanges.txt";
		::removeAndWriteFile(path_solution_name, rmp.to_string_integer_solution());
		//string path_solution_stats_name = ::solutionPath + ::instanceName + "_sol_stats_exchanges.csv";
		//bpc.checker_solution_print(path_instance_name);
		//bpc.solution_stats_print(path_solution_stats_name, this->fileName);
		
		//PRINT CSV WITH SOLUTION REPORT
		int clique_number = clique_inequalities->compute_violated_inequalities_number();
		int oh_number = odd_hole->compute_violated_inequalities_number();

		double oh_avg_length = odd_hole->get_avg_length() / oh_number;
		double clique_avg_length = clique_inequalities->get_avg_length() / clique_number;

		double db_root_before_oh = odd_hole->compute_dual_bound_before_first();
		if (db_root_before_oh < 0) {
			db_root_before_oh = bpc.get_dual_bound_root_before_cuts();
		}

		this->csv_line = to_string(this->instance->getTimeFloydWarshall()) + "," + to_string(this->instance->getRemovedArcsFW()) + "," + to_string(this->instance->getRemovedNodesFW())
			+ "," + to_string(this->instance->getTimeOmerPreprocessing()) + "," + to_string(this->instance->getPairGraphsOP()) + "," + to_string(this->instance->getAvgNodesGraphsOP()) + "," + to_string(this->instance->getAvgArcsGraphsOP())
			+ "," + to_string(bpc.get_nb_explored_nodes()) + "," + to_string(bpc.get_nb_columns()) + "," + to_string(::labellingCalls)
			+ "," + to_string(clique_number) + "," + to_string(oh_number) + "," + to_string(clique_inequalities->compute_separation_time())
			+ "," + to_string(odd_hole->compute_separation_time()) + "," + to_string(clique_inequalities->compute_minimal_violation()) + "," + to_string(clique_inequalities->compute_maximal_violation())
			+ "," + to_string(odd_hole->compute_minimal_violation()) + "," + to_string(odd_hole->compute_maximal_violation()) + "," + to_string(clique_avg_length)
			+ "," + to_string(oh_avg_length)
			+ "," + to_string(bpc.get_dual_bound_root_before_cuts()) + "," + to_string(db_root_before_oh)
			+ "," + to_string(bpc.get_dual_bound_root_after_cuts()) + "," + to_string(bpc.get_best_bound())
			+ "," + to_string(::LB_before_final_MIP) + "," + to_string(::gap_final_MIP) + "," + to_string(::time_final_MIP) + "," + to_string(bpc.get_best_incumbent_value())
			+ "," + to_string(bpc.get_gap()) + "," + to_string(this->time) + "\n";

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

	}

	//We free the memory if needed of Gurobi model and environment 
	bpc.free_memory();
	rmp.free_memory();

	//We free memory of created methods
	//delete cycles;
	delete odd_hole;
	//delete clique_inequalities;
	//delete heuristic_generator;
	delete exact_generator;
	delete arcBranch;
	
}

const double& KEPsolver::getValue() const {
	return this->value;
}
const double& KEPsolver::getBound() const {
	return this->bound;
}
const double& KEPsolver::getTime() const {
	return this->time;
}
const vector<Exchange>& KEPsolver::getSolution() const {
	return this->solution;
}

const string& KEPsolver::get_length_stats() const {
	return this->length_stats;
}

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

const string KEPsolver::toString() const {
	string str;
	str += "KEP INSTANCE:\n";
	str += this->instance->toString(false) + "\n";
	return str;
}


