/*Implementation of PStep_Solution.h

It contains the member functions of PStep_Solution
*/

#include "PStep_Solution.h"

//Constructor functions:
//------------------------------------------------------------------------------------

//Default Constructor
PStep_Solution::PStep_Solution(){

	//Value by which each edge is selected
	EdgeFraction = 0;

	//Solution value
	SolutionValue = 0;

	//Number of locations
	NumberOfLocations = 0;

	//Time of construction
	ConstructionTime = HomeMadeclock::now();

}

//Constructor that sets the number of locations and initializes EdgeFraction.
PStep_Solution::PStep_Solution(short int newNumberOfLocations){

	//Value by which each edge is selected
	EdgeFraction = 0;

	//Solution value
	SolutionValue = 0;

	//Number of locations
	NumberOfLocations = newNumberOfLocations;

	//Time of construction
	ConstructionTime = HomeMadeclock::now();

	//Set instance dependent settings
	InitializeEdgeFraction();

}

//Copy Constructor
PStep_Solution::PStep_Solution(const PStep_Solution &OriginalPStep_Solution){

	CopyEmptyAssumed(OriginalPStep_Solution);

}

//Operator functions:
//------------------------------------------------------------------------------------

//Assignment operator
PStep_Solution PStep_Solution::operator =(PStep_Solution OriginalPStep_Solution){

	EmptySolution();
	CopyEmptyAssumed(OriginalPStep_Solution);

	return *this;

}

//Other functions
//------------------------------------------------------------------------------------

//Check whether the current solution is integer upto tolerance level Tolerance.
bool PStep_Solution::IsInteger(double Tolerance){

	//Go over all EdgeFractions
	short int Index = 0;
	for (short int j = 1; j < NumberOfLocations; ++j){
		//Check arcs from the depot
		if (!(EdgeFraction[Index] <= Tolerance || (EdgeFraction[Index]>=1-Tolerance && EdgeFraction[Index]<=1+Tolerance) || EdgeFraction[Index]>=2-Tolerance)){
			return false;
		}
		++Index;
		//Check arcs not from the depot
		for (short int i = 1; i < j; ++i){
			if (!(EdgeFraction[Index] <= Tolerance || EdgeFraction[Index]>=1 - Tolerance)){
				return false;
			}
			++Index;
		}
	}

	return true;

}

//Initialize EdgeFraction, all values are set to zero.
//NOTE: it is assumed that EdgeFraction is not initialized yet,
//while NumberOfLocations is set to the proper value.
void PStep_Solution::InitializeEdgeFraction(){

	//Initialize ArcFraction
	EdgeFraction = new double[NumberOfLocations*(NumberOfLocations - 1) / 2];
	for (short int i = 0; i < NumberOfLocations*(NumberOfLocations - 1) / 2; ++i){
		EdgeFraction[i] = 0;
	}

}

//Reinitialize the current solution, with the same NumberOfLocations.
//It invokes EmptySolution and InitializeEdgeFraction.
void PStep_Solution::Reinitialize(){

	//Preserve NumberOfLocations
	short int TempNumberOfLocations = NumberOfLocations;

	//Reinitialize
	EmptySolution();
	NumberOfLocations = TempNumberOfLocations;
	InitializeEdgeFraction();

}

//Print functions
//------------------------------------------------------------------------------------

//Print solution
void PStep_Solution::PrintSolution(){

	//Print
	std::cout << "Solution Value:   " << SolutionValue << std::endl;
	std::cout << "Selected p-steps: " << std::endl;
	std::cout << "-------------------------------------------------" << std::endl << std::endl;
	//Support
	int Index = 0;
	std::list<double>::iterator CurrentPStepSelection = PStepSelection.begin();
	for (auto CurrentPStep = PSteps.begin(); CurrentPStep!=PSteps.end(); ++CurrentPStep){
		std::cout << "p-step " << Index << " (" << *CurrentPStepSelection << ")" << std::endl;
		CurrentPStep->PrintPStep();
		std::cout << std::endl << "-------------------------------------------------" << std::endl << std::endl;
		++Index;
		std::advance(CurrentPStepSelection,1);
	}

}

//Print the edge fraction
void PStep_Solution::PrintEdgeFraction(){

	//Check whether the current print function is sufficient
	if (NumberOfLocations > 9999){
		std::cout << "Too many Locations to print" << std::endl;
		return;
	}

	//Print Table Header
	std::cout << "     ";
	for (short int i = 0; i < NumberOfLocations; ++i){
		std::cout << " " << std::setw(4) << i;
	}
	std::cout << std::endl;

	//Support
	std::cout << std::fixed << std::setprecision(2);
	//Print EdgeFractions
	for (short int i = 0; i < NumberOfLocations; ++i){
		std::cout << std::setw(4) << i << " ";
		for (short int j = 0; j <= i; ++j){
			std::cout << "-----";
		}
		
		for (short int j = i+1; j < NumberOfLocations; ++j){
			std::cout << " " << std::setw(4) << EdgeFraction[(j-1)*j/2+i];
		}
		std::cout << std::endl;
	}

}

//Write functions
//------------------------------------------------------------------------------------

//Function to write the solution to file. The format can be deduced from the implementation.
//It is assumed that all members are set properly to represent a feasible (integer) solution.
//The orientation of the routes is arbitrary and might not match the selected p-steps.
void PStep_Solution::Write(const char* FileName){

	//Create and open file
	std::ofstream File(FileName);
	//Check whether the file could be opened
	if (!File){
		std::cout << "Cannot open file " << FileName << "." << std::endl;
		return;
	}

	//Write the solution value
	File << "Solution_Value: " << SolutionValue << std::endl;

	//Write the routes
	File << "Routes:";
	//Initialize list to keep track of which locations are still eligible to
	//be a NextLocation
	bool *LocationEligible = new bool[NumberOfLocations];
	for (short int i = 0; i < NumberOfLocations; ++i){
		LocationEligible[i] = true;
	}
	//Initialize support variables
	int RouteCounter = 1;
	short int NextLocation;
	//Find start location of the route
	for (short int i = 1; i<NumberOfLocations; i++){
		//Check if location i is eligible
		if (LocationEligible[i]){
			//Check if a return trip to i is made
			if (EdgeFraction[(i - 1)*i/2]>1.5){
				//Print route
				File << std::endl << "Route " << RouteCounter << ": " << i;
				//Update support variables
				RouteCounter++;
				LocationEligible[i] = false;
			}
			//If no return trip is made, check whether a route does start from i
			else if (EdgeFraction[(i - 1)*i/2]>0.5){
				//Print a new route start
				File << std::endl << "Route " << RouteCounter << ": ";
				//Update support variables
				RouteCounter++;
				NextLocation = i;
				LocationEligible[i] = false;				
				//Temporarily make the depot ineligble, such that the arc back to the depot is
				//not immediately selected when a new route is started.
				LocationEligible[0] = false;
				//Append the next location and find a new one, until the next location is the depot
				while (NextLocation != 0 && NextLocation < NumberOfLocations){
					//Print:
					//Check whether the NextLocation is not the first on the route
					if (LocationEligible[0]){
						//It is not the first
						File << " - " << NextLocation;
					}
					else{
						//It is the first
						File << NextLocation;
					}

					//Find the new NextLocation:
					//Initialize support variable
					bool NextNotFound = true;
					//Go over all edges for which j<NextLocation
					for (short int j = 0; j<NextLocation && NextNotFound; ++j){
						if (LocationEligible[j] && EdgeFraction[(NextLocation - 1)*NextLocation/2 + j]>0.5){
							//Update support variables
							NextLocation = j;
							LocationEligible[j] = false;
							NextNotFound = false;;
						}
					}
					//Go over all edges for which j>i
					for (short int j = NextLocation + 1; j < NumberOfLocations && NextNotFound; ++j){
						if (LocationEligible[j] && EdgeFraction[(j - 1)*j / 2 + NextLocation]>0.5){
							//Update support variables
							NextLocation = j;
							LocationEligible[j] = false;
							NextNotFound=false;
						}
					}
					//Make the depot eligible again
					LocationEligible[0] = true;
				}

				//Check for a mistake
				if (NextLocation >= NumberOfLocations){
					File << "ERROR" << std::endl;
					File.close();
					return;
				}

			}
		}//end check whether the route is eligible
	}//end go over potential start locations

	File.close();

	//clean
	delete[]LocationEligible;

}

//Destructor functions
//------------------------------------------------------------------------------------

//Default destructor
PStep_Solution::~PStep_Solution(){

	EmptySolution();

}

//Auxiliary functions
//------------------------------------------------------------------------------------

//Empty all entries
void PStep_Solution::EmptySolution(){

	//delete
	PSteps.clear();
	PStepSelection.clear();
	SolutionValue = 0;
	if (!(EdgeFraction == 0 || NumberOfLocations == 0)){
		delete[]EdgeFraction;
	}
	EdgeFraction = 0;
	NumberOfLocations = 0;

}

//Copy all entries
//IMPORTANT: it is assumed that the current solution is empty!
void PStep_Solution::CopyEmptyAssumed(const PStep_Solution &OriginalPStep_Solution){

	//Copy
	NumberOfLocations = OriginalPStep_Solution.NumberOfLocations;
	SolutionValue = OriginalPStep_Solution.SolutionValue;
	PSteps = OriginalPStep_Solution.PSteps;
	PStepSelection = OriginalPStep_Solution.PStepSelection;
	if (!(OriginalPStep_Solution.EdgeFraction == 0 || NumberOfLocations == 0)){
		EdgeFraction = new double[NumberOfLocations*(NumberOfLocations - 1) / 2];
		for (short int i = 0; i<NumberOfLocations*(NumberOfLocations - 1) / 2; ++i){
			EdgeFraction[i] = OriginalPStep_Solution.EdgeFraction[i];
		}
	}
	else{
		EdgeFraction = 0;
	}
	ConstructionTime = OriginalPStep_Solution.ConstructionTime;

}