/*Implementation of CVRPInstance.h

It contains the member functions of CVRPInstanceGenerationSettings.
It contains the member functions of CVRPInstance.

*/

#include "CVRPInstance.h"

//CVRPInstanceGenerationSettings

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

//Default constructor, creates empty settings
CVRPInstanceGenerationSettings::CVRPInstanceGenerationSettings(){

	//Set all settings to 0
	NumberOfCustomers = 0;
	CustomerGenerationType = 0;
	CG1SquareSide = 0;
	TravelCostType = 0;
	VehicleCapacity = 0;
	DemandDistribution = 0;
	DemandUniformLB = 0;
	DemandUniformUB = 0;

}

//Basic Constructor 1:
CVRPInstanceGenerationSettings::CVRPInstanceGenerationSettings(short int newNumberOfCustomers, double newVehicleCapacity, double newDemandUniformLB, double newDemandUniformUB){

	//Set settings
	NumberOfCustomers = newNumberOfCustomers;
	CustomerGenerationType = 1;
	CG1SquareSide = 5;
	TravelCostType = 1;
	VehicleCapacity = newVehicleCapacity;
	DemandDistribution = 1;
	DemandUniformLB = newDemandUniformLB;
	DemandUniformUB = newDemandUniformUB;

}

//====================================================================================

//CVRPInstance

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

//Default constructor, creates an empty instance
CVRPInstance::CVRPInstance(){

	//Set all instance parameters to 0
	NumberOfCustomers = 0;
	VehicleCapacity = 0;
	NumberOfVehiclesIsFixed = false;
	FixedNumberOfVehicles = 0;
	LocationCoordinates = 0;
	TravelCosts = 0;
	Demand = 0;

}

//Copy constructor of CVRPInstance
CVRPInstance::CVRPInstance(const CVRPInstance &OriginalCVRPInstance){

	CopyEmptyAssumed(OriginalCVRPInstance);

}

//Construct an instance based on CVRPInstanceGenerationSettings
CVRPInstance::CVRPInstance(CVRPInstanceGenerationSettings IGS){

	//Set basic parameters
	NumberOfCustomers = IGS.NumberOfCustomers;
	VehicleCapacity = IGS.VehicleCapacity;
	NumberOfVehiclesIsFixed = false;
	FixedNumberOfVehicles = 0;

	//GenerateCoordinates:
	//Check customer generation type
	if (IGS.CustomerGenerationType == 1){
		GenerateLocationCoordinates1(IGS.CG1SquareSide);
	}

	//Generate demand expectation
	if (IGS.DemandDistribution == 1){
		GenerateDemand1(IGS);
	}

	//Generate TravelCosts
	//Check fixedtravel cost type
	if (IGS.TravelCostType == 1){
		GenerateTravelCosts1();
	}

}

//Load an instance from a .txt file, the format can be deduced from the implementation
CVRPInstance::CVRPInstance(const char *FileName){

	//Open input file
	std::ifstream File(FileName);
	//Check whether the file could be opened
	if (!File){
		std::cout << "Cannot open file at " << FileName << "." << std::endl;
		return;
	}

	//Initialize support variable
	std::string Support;

	//Read the number of Customers
	File >> Support >> NumberOfCustomers;

	//Read the vehicle capacity
	File >> Support >> VehicleCapacity;
	
	//Read the fixed number of vehicles
	double DoubleSupport;
	File >> Support >> DoubleSupport;
	if (DoubleSupport == 1){
		NumberOfVehiclesIsFixed = true;
	}
	else{
		NumberOfVehiclesIsFixed = false;
	}
	File >> Support >> FixedNumberOfVehicles;

	//Read the location coordinates
	File >> Support;
	LocationCoordinates = new double*[NumberOfCustomers + 1];
	for (short int i = 0; i<NumberOfCustomers + 1; i++){
		LocationCoordinates[i] = new double[2];
		File >> Support >> LocationCoordinates[i][0] >> LocationCoordinates[i][1];
	}

	//Read Demand
	File >> Support;
	Demand = new double[NumberOfCustomers];
	for (short int i = 0; i<NumberOfCustomers; ++i){
		File >> Support >> Demand[i];
	}

	//Read travel costs
	File >> Support;
	TravelCosts = new double*[NumberOfCustomers + 1];
	for (short int i = 0; i<NumberOfCustomers + 1; ++i){
		TravelCosts[i] = new double[NumberOfCustomers + 1];
		for (short int j = 0; j<NumberOfCustomers + 1; ++j){
			File >> TravelCosts[i][j];
		}
	}

	File.close();

}

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

//= operator of CVRPInstance
CVRPInstance CVRPInstance::operator=(CVRPInstance OriginalCVRPInstance){

	EmptyInstance();
	CopyEmptyAssumed(OriginalCVRPInstance);
	return *this;

}

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

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

	EmptyInstance();

}

//Converter functions
//------------------------------------------------------------------------------------


//Convert CVRP E with coordinates to a CVRP instance
//Read an E coordinates instance and write a CVRPInstance.
//The costs per arc are rounded to the nearest number with DecimalPlaces decimal places.
void CVRPInstance::ConvertECoordinatesToCVRPInstance(const char* EFileName, const char* ConvertedFileName, short int NewFixedNumberOfVehicles, short int DecimalPlaces){
	
	//Open input file
	std::ifstream File(EFileName);
	//Check whether the file could be opened
	if (!File){
		std::cout << "Cannot open file at " << EFileName << "." << std::endl;
		return;
	}

	//Initialize support variable
	std::string Support;

	//Go through redundant info
	for (short int i = 0; i < 21; ++i){
		File >> Support;
	}

	//Read the number of Customers
	File >> NumberOfCustomers;
	--NumberOfCustomers;

	//Go through redundant info
	for (short int i = 0; i < 5; ++i){
		File >> Support;
	}

	//Read the vehicle capacity
	File >> VehicleCapacity;

	//Set fixed number of vehicles
	NumberOfVehiclesIsFixed = true;
	FixedNumberOfVehicles = NewFixedNumberOfVehicles;

	//Read the location coordinates
	File >> Support;
	LocationCoordinates = new double*[NumberOfCustomers + 1];
	for (short int i = 0; i<NumberOfCustomers + 1; i++){
		LocationCoordinates[i] = new double[2];
		File >> Support >> LocationCoordinates[i][0] >> LocationCoordinates[i][1];
	}

	//Set travel costs
	//Initialize traveling costs, 0 represents the depot
	TravelCosts = new double*[NumberOfCustomers + 1];
	//Compute rounding support
	double RoundingSupport = std::pow(10, DecimalPlaces);
	//Go over locations
	for (short int i = 0; i <= NumberOfCustomers; ++i){
		TravelCosts[i] = new double[NumberOfCustomers + 1];
		//Go over locations
		for (short int j = 0; j <= NumberOfCustomers; ++j){
			//Calculate distance between location i and j
			if (i == j){
				TravelCosts[i][j] = 0;
			}
			else{
				TravelCosts[i][j] = std::round(RoundingSupport*sqrt(pow(LocationCoordinates[i][0] - LocationCoordinates[j][0], 2) + pow(LocationCoordinates[i][1] - LocationCoordinates[j][1], 2))) / RoundingSupport;
			}
		}
	}

	//Go through redundant info
	for (short int i = 0; i < 3; ++i){
		File >> Support;
	}

	//Read Demand
	Demand = new double[NumberOfCustomers];
	for (short int i = 0; i<NumberOfCustomers; ++i){
		File >> Support >> Demand[i];
	}

	File.close();

	//Write converted instance to file.
	Write(ConvertedFileName);

}

//Convert CVRP E functions
//Read an E instance and write a CVRPInstance
void CVRPInstance::ConvertEEdgeToCVRPInstance(const char* EFileName, const char* ConvertedFileName, short int NewFixedNumberOfVehicles){

	//Open input file
	std::ifstream File(EFileName);
	//Check whether the file could be opened
	if (!File){
		std::cout << "Cannot open file at " << EFileName << "." << std::endl;
		return;
	}

	//Initialize support variable
	std::string Support;

	//Go through redundant info
	for (short int i = 0; i < 21; ++i){
		File >> Support;
	}

	//Read the number of Customers
	File >> NumberOfCustomers;
	--NumberOfCustomers;

	//Go through redundant info
	for (short int i = 0; i < 9; ++i){
		File >> Support;
	}

	//Read the vehicle capacity
	File >> VehicleCapacity;

	//Set fixed number of vehicles
	NumberOfVehiclesIsFixed = true;
	FixedNumberOfVehicles = NewFixedNumberOfVehicles;

	//Set bogus location coordinates
	LocationCoordinates = new double*[NumberOfCustomers + 1];
	for (short int i = 0; i<NumberOfCustomers + 1; i++){
		LocationCoordinates[i] = new double[2];
		LocationCoordinates[i][0] = 0;
		LocationCoordinates[i][1] = 0;
	}

	//Read travel costs
	File >> Support;
	TravelCosts = new double*[NumberOfCustomers + 1];
	for (short int i = 0; i<NumberOfCustomers + 1; ++i){
		TravelCosts[i] = new double[NumberOfCustomers + 1];
		for (short int j = 0; j<NumberOfCustomers + 1; ++j){
			TravelCosts[i][j]=0;
		}
	}
	for (short int i = 0; i<NumberOfCustomers+1 ; ++i){
		for (short int j = 0; j<i; ++j){
			File >> TravelCosts[i][j];
			TravelCosts[j][i] = TravelCosts[i][j];
		}
	}

	//Go through redundant info
	for (short int i = 0; i < 3; ++i){
		File >> Support;
	}

	//Read Demand
	Demand = new double[NumberOfCustomers];
	for (short int i = 0; i<NumberOfCustomers; ++i){
		File >> Support >> Demand[i];
	}

	File.close();

	//Write converted instance to file.
	Write(ConvertedFileName);

}

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

//Function to write the CVRP instance to file. The format can be deduced from the implementation.
void CVRPInstance::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;
	}
	//Fix the number of decimal places
	File << std::fixed;
	File.precision(1);

	//Write the number of Customers
	File << "Number_Of_Customers:          " << NumberOfCustomers << std::endl;

	//Write the vehicle capacity
	File << "Vehicle_Capacity:             " << VehicleCapacity << std::endl;

	//Write the fixed number of vehicles
	File << "Number_Of_Vehicles_are_fixed: ";
	if (NumberOfVehiclesIsFixed){
		File << "1" << std::endl;
	}
	else{
		File << "0" << std::endl;
	}
	File << "Number_Of_Vehicles:           " << FixedNumberOfVehicles << std::endl;

	//Write the location coordinates
	File.precision(4);
	File << std::endl << "Location_Coordinates:" << std::endl;
	for (short int i = 0; i<NumberOfCustomers + 1; i++){
		File << i << "\t" << LocationCoordinates[i][0] << "\t" << LocationCoordinates[i][1] << std::endl;
	}

	//Write demand
	File.precision(1);
	File << std::endl << "Demand:" << std::endl;
	for (short int i = 0; i<NumberOfCustomers; ++i){
		File << i + 1 << "\t" << Demand[i] << std::endl;
	}

	//Write travel costs
	File.precision(2);
	File << std::endl << "Fixed_Travel_Costs:" << std::endl;
	for (short int i = 0; i<NumberOfCustomers + 1; ++i){
		for (short int j = 0; j<NumberOfCustomers + 1; ++j){
			File << TravelCosts[i][j] << "\t";
		}
		File << std::endl;
	}

	File.close();

}

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

//Empty all entries
void CVRPInstance::EmptyInstance(){

	//Check whether the current instance contains any customers
	if (NumberOfCustomers>0){

		//Delete LocationCoordinates
		if (!(LocationCoordinates == 0)){
			//Go over all customers
			for (short int i = 0; i<NumberOfCustomers + 1; i++){
				delete[]LocationCoordinates[i];
			}
			delete[]LocationCoordinates;
			LocationCoordinates = 0;
		}
		//Delete Demand
		if (!(Demand == 0)){
			delete[]Demand;
			Demand = 0;
		}
		//Delete TravelCosts
		if (!(TravelCosts == 0)){
			//Go over all customers
			for (short int i = 0; i<NumberOfCustomers + 1; i++){
				delete[]TravelCosts[i];
			}
			delete[]TravelCosts;
			TravelCosts = 0;
		}
	}

	//Reset other parameters
	NumberOfCustomers = 0;
	VehicleCapacity = 0;
	NumberOfVehiclesIsFixed = false;
	FixedNumberOfVehicles = 0;

}

//Copy all entries
//IMPORTANT: it is assumed that the current instance is empty!
//It is also assumed that NumberOfCustomers and NumberOfScenarios correspond to the array lengths
void CVRPInstance::CopyEmptyAssumed(const CVRPInstance &OriginalCVRPInstance){

	//copy
	NumberOfCustomers = OriginalCVRPInstance.NumberOfCustomers;
	VehicleCapacity = OriginalCVRPInstance.VehicleCapacity;
	NumberOfVehiclesIsFixed = OriginalCVRPInstance.NumberOfVehiclesIsFixed;
	FixedNumberOfVehicles = OriginalCVRPInstance.FixedNumberOfVehicles;
	if (!(OriginalCVRPInstance.LocationCoordinates == 0)){
		LocationCoordinates = new double*[NumberOfCustomers + 1];
		for (short int i = 0; i<NumberOfCustomers + 1; i++){
			LocationCoordinates[i] = new double[2];
			LocationCoordinates[i][0] = OriginalCVRPInstance.LocationCoordinates[i][0];
			LocationCoordinates[i][1] = OriginalCVRPInstance.LocationCoordinates[i][1];
		}
	}
	else{
		LocationCoordinates = 0;
	}
	if (!(OriginalCVRPInstance.Demand == 0)){
		Demand = new double[NumberOfCustomers];
		for (short int i = 0; i<NumberOfCustomers; i++){
			Demand[i] = OriginalCVRPInstance.Demand[i];
		}
	}
	else{
		Demand = 0;
	}
	if (!(OriginalCVRPInstance.TravelCosts == 0)){
		TravelCosts = new double*[NumberOfCustomers + 1];
		for (short int i = 0; i<NumberOfCustomers + 1; i++){
			TravelCosts[i] = new double[NumberOfCustomers + 1];
			for (short int j = 0; j<NumberOfCustomers + 1; j++){
				TravelCosts[i][j] = OriginalCVRPInstance.TravelCosts[i][j];
			}
		}
	}
	else{
		TravelCosts = 0;
	}

}

//Generate location coordinates according to type 1
//Uniformly in a square with the depot at the center.
//Coordinates are rounded to 4 decimal places.
void CVRPInstance::GenerateLocationCoordinates1(double SquareSide){

	//Initialize random number generator
	std::uniform_real_distribution<double> LocationDistribution(-SquareSide / 2, SquareSide / 2);

	//Initialize location coordinates
	LocationCoordinates = new double*[NumberOfCustomers + 1];
	//Define depot coordinates
	LocationCoordinates[0] = new double[2];
	LocationCoordinates[0][0] = 0;
	LocationCoordinates[0][1] = 0;
	for (short int i = 1; i<NumberOfCustomers + 1; ++i){
		//Generate location per customer
		LocationCoordinates[i] = new double[2];
		LocationCoordinates[i][0] = ceil(10000 * LocationDistribution(Default_Random_Engine)) / 10000;
		LocationCoordinates[i][1] = ceil(10000 * LocationDistribution(Default_Random_Engine)) / 10000;
	}

}

//Generate demand according to structure 1
//Uniformly distributed rounded up to the nearest integer.
void CVRPInstance::GenerateDemand1(CVRPInstanceGenerationSettings IGS){

	//Initialize random number generator
	std::uniform_real_distribution<double> Distribution(IGS.DemandUniformLB, IGS.DemandUniformUB);

	//Initialize expected demand, 0 represents the first customer
	Demand = new double[NumberOfCustomers];
	//Go over all customers
	for (short int i = 0; i < NumberOfCustomers; ++i){
		//Generate demand expectation
		Demand[i] = ceil( Distribution(Default_Random_Engine));
	}

}

//Generate travel costs according to type 1
//Euclidean distance
void CVRPInstance::GenerateTravelCosts1(){

	//Initialize traveling costs, 0 represents the depot
	TravelCosts = new double*[NumberOfCustomers + 1];
	//Go over locations
	for (short int i = 0; i <= NumberOfCustomers; ++i){
		TravelCosts[i] = new double[NumberOfCustomers + 1];
		//Go over locations
		for (short int j = 0; j <= NumberOfCustomers; ++j){
			//Calculate distance between location i and j
			if (i == j){
				TravelCosts[i][j] = 0;
			}
			else{
				TravelCosts[i][j] = ceil(100 * sqrt(pow(LocationCoordinates[i][0] - LocationCoordinates[j][0], 2) + pow(LocationCoordinates[i][1] - LocationCoordinates[j][1], 2))) / 100;
			}
		}
	}

}