/*Implementation of PStep_Algorithm.h

It contains the p-step functions of PStep_Algorithm

*/

#include "PStep_Algorithm.h"

//p-step functions:
//------------------------------------------------------------------------------------

//Select all p-steps from AlgorithmMembers.PSteps that are feasible in the current node,
//based on CurrentTreeNode->EdgeInUse, and put pointers to them into CurrentRoutes.
//CurrentRoutes is emptied at the start of this function
void PStep_Algorithm::SelectCurrentPSteps(){

	//Empty CurrentPSteps and the shortlist
	AlgorithmMembers.CurrentPSteps.clear();
	AlgorithmMembers.CurrentPStepsxIndices.clear();
	AlgorithmMembers.NumberOfCurrentPSteps = 0;
	AlgorithmMembers.PStepShortList.clear();
	AlgorithmMembers.NumberOfPStepsInShortList = 0;

	//Go over all psteps
	for (auto CurrentPStep = AlgorithmMembers.PSteps.begin(); CurrentPStep != AlgorithmMembers.PSteps.end();++CurrentPStep){

		//Check whether the current route is feasible in the current node
		if (PStepIsFeasible(CurrentPStep)){
			//Add the current route
			AlgorithmMembers.CurrentPSteps.push_back(CurrentPStep);
			++AlgorithmMembers.NumberOfCurrentPSteps;
		}

	}//end go over all routes

}

//Check whether a p-pstep is feasible in the current node
bool PStep_Algorithm::PStepIsFeasible(std::list<PStep>::iterator CurrentPStep){
	
	//Check whether the edges can be used:
	//Obtain the first locations on the p-step
	short int PreviousLocation = *CurrentPStep->Locations.begin();
	short int NextLocation;
	//Go over the nodes on the p-step
	for (auto CurrentLocation = ++CurrentPStep->Locations.begin(); CurrentLocation != CurrentPStep->Locations.end();++CurrentLocation){
		//Iterate 
		NextLocation = *CurrentLocation;
		//Check whether the arc is not in use
		if (!(AlgorithmMembers.CurrentTreeNode->EdgeInUse[AlgorithmMembers.Arcs[PreviousLocation][NextLocation].Index])){
			return false;
		}
		//Update previous location
		PreviousLocation = NextLocation;
	}

	return true;

}

//Create an IloNumColumn representing a variable corresponding to a p-step.
//It takes care of the values in the
//-Objective
//-CustomerAssignmentConstraints
//-DegreeConstraints
//-DemandFlowConstraints
//-EdgeFlowConstraints
//The resulting column is stored in Column.
void PStep_Algorithm::CreatePStepColumn(PStep &CurrentPStep, IloObjective &Objective, IloRangeArray &CustomerAssignmentConstraints, IloRangeArray &DegreeConstraints, IloRangeArray &DemandFlowConstraints, IloRangeArray &EdgeFlowConstraints, IloNumColumn &Column){

	//Add x to the objective function
	Column += Objective(CurrentPStep.Cost);

	//Check whether the p-step is a return trip to the depot
	if (CurrentPStep.Locations.front() == 0 && CurrentPStep.Locations.back() == 0 && CurrentPStep.Locations.size() == 3){
		//The current p-step is a return trip to the depot
		
		//Obtain the customer
		short int Customer = *(++(CurrentPStep.Locations.begin()));
		//add x to the assignment and edge flow constraints
		Column += CustomerAssignmentConstraints[Customer - 1](2);
		Column += EdgeFlowConstraints[AlgorithmMembers.Arcs[0][Customer].Index](2);

	}
	//Check whether ng-path relaxation is applied
	else if (AlgorithmMembers.AlgorithmSettings.UseNGPathRelaxation){
		//Ng-path relaxation is applied and the current p-step is not a return trip to the depot.

		//Obtain number of customers
		short int NumberOfCustomers = AlgorithmMembers.Instance.NumberOfCustomers;
		//Obtain arcs
		std::vector<std::vector<PStep_Arc>>& Arcs = AlgorithmMembers.Arcs;

		//Initialize array which keeps track of the coefficient in the CustomerAssignmentConstraints
		//for each customer
		short int* CustomerAssignmentConstraintsCoefficient = new short int[NumberOfCustomers];
		//Initialize all coefficients as 0.
		for (short int i = 0; i < NumberOfCustomers; ++i){
			CustomerAssignmentConstraintsCoefficient[i] = 0;
		}
		//Initialize vector which keeps track of the coefficient in the EdgeFlowConstraints.
		//EdgeFlowConstraintsCoefficient[i][0] gives the index of the i-th edge in the p-step.
		//EdgeFlowConstraintsCoefficient[i][1] gives the amount of times the corresponding arc is used.
		std::vector<std::vector<short int>> EdgeFlowConstraintsCoefficient;

		//Add x to the assignment, degree, demand flow and edge flow constraints:
		//Obtain first location and store it in PreviousLocation, and initialize NextLocation
		short int PreviousLocation = CurrentPStep.Locations.front();
		short int NextLocation;
		//Check whether the first location is not the depot
		if (PreviousLocation > 0){
			//Update customer assignment constraints coefficient
			CustomerAssignmentConstraintsCoefficient[PreviousLocation - 1]+=1;
			//Add x to the degree and demand flow constraints
			Column += DegreeConstraints[PreviousLocation - 1](1);
			Column += DemandFlowConstraints[PreviousLocation - 1](CurrentPStep.StartingLoad);
		}
		//Go over all middle locations (i.e., not first and last)
		for (auto CurrentLocation = ++(CurrentPStep.Locations.begin()); CurrentLocation != --(CurrentPStep.Locations.end()); ++CurrentLocation){
			
			//Iterate NextLocation;
			NextLocation = *CurrentLocation;
			//Update customer assignment constraints coefficient
			CustomerAssignmentConstraintsCoefficient[NextLocation - 1]+=2;
			
			//Add x to the edge flow constraints:
			//Obtain arc index
			short int CurrentIndex = Arcs[PreviousLocation][NextLocation].Index;
			//Check whether the current index has already been used:
			//Initialize indicator indicating whether the current index has not already been used
			bool CurrentIndexNotUsedYet = true;
			//Go over all EdgeFlowConstraintsCoefficient
			for (auto CurrentCoefficient = EdgeFlowConstraintsCoefficient.begin(); (CurrentCoefficient != EdgeFlowConstraintsCoefficient.end()) && CurrentIndexNotUsedYet; ++CurrentCoefficient){
				//Check whether the current index is the same as that of the current coefficient
				if (CurrentCoefficient->at(0) == CurrentIndex){
					//The current index is the same
					CurrentCoefficient->at(1) += 1;
					//Update indicator
					CurrentIndexNotUsedYet = false;
				}
			}
			//Check whether the index has notalready been used
			if (CurrentIndexNotUsedYet){
				//The current index is new, add:
				//Initialize coefficient
				std::vector<short int> NewCoefficient;
				//Add index
				NewCoefficient.push_back(CurrentIndex);
				//Add coefficient
				NewCoefficient.push_back(1);
				//Add to list
				EdgeFlowConstraintsCoefficient.push_back(NewCoefficient);
			}
			
			//Update first location
			PreviousLocation = NextLocation;
		}
		//Obtain last location
		short int LastLocation = CurrentPStep.Locations.back();
		//Check whether the last location is not the depot
		if (LastLocation > 0){
			//Add x to the assignment, degree, and demand flow constraints
			CustomerAssignmentConstraintsCoefficient[LastLocation - 1]+=1;
			Column += DegreeConstraints[LastLocation - 1](-1);
			Column += DemandFlowConstraints[LastLocation - 1](-(CurrentPStep.TotalLoad));
		}
		//Add x to the edge flow constraints:
		//Obtain arc index
		short int CurrentIndex = Arcs[PreviousLocation][LastLocation].Index;
		//Check whether the current index has already been used:
		//Initialize indicator indicating whether the current index has not already been used
		bool CurrentIndexNotUsedYet = true;
		//Go over all EdgeFlowConstraintsCoefficient
		for (auto CurrentCoefficient = EdgeFlowConstraintsCoefficient.begin(); (CurrentCoefficient != EdgeFlowConstraintsCoefficient.end()) && CurrentIndexNotUsedYet; ++CurrentCoefficient){
			//Check whether the current index is the same as that of the current coefficient
			if (CurrentCoefficient->at(0) == CurrentIndex){
				//The current index is the same
				CurrentCoefficient->at(1) += 1;
				//Update indicator
				CurrentIndexNotUsedYet = false;
			}
		}
		//Check whether the index has not already been used
		if (CurrentIndexNotUsedYet){
			//The current index is new, add:
			//Initialize coefficient
			std::vector<short int> NewCoefficient;
			//Add index
			NewCoefficient.push_back(CurrentIndex);
			//Add coefficient
			NewCoefficient.push_back(1);
			//Add to list
			EdgeFlowConstraintsCoefficient.push_back(NewCoefficient);
		}

		//Add x to the customer assignment constraints:
		//Go over all customers
		for (short int i = 0; i < NumberOfCustomers; ++i){
			//Obtain coefficient
			short int Coefficient = CustomerAssignmentConstraintsCoefficient[i];
			//Check whether the CustomerAssignmentConstraintCoefficient is not zero
			if (Coefficient > 0){
				//Add x to the assignment constraints
				Column += CustomerAssignmentConstraints[i](Coefficient);
			}
		}

		//Add x to the edge flow constraints:
		//Go over all EdgeFlowConstraintsCoefficient
		for (auto CurrentCoefficient = EdgeFlowConstraintsCoefficient.begin(); CurrentCoefficient != EdgeFlowConstraintsCoefficient.end(); ++CurrentCoefficient){
			//Add x to the edge flow constraints
			Column += EdgeFlowConstraints[CurrentCoefficient->at(0)](CurrentCoefficient->at(1));
		}

		//clean
		delete[]CustomerAssignmentConstraintsCoefficient;

	}
	else{
		//Ng-path relaxation is not applied and the current p-step is not a return trip to the depot.

		//Add x to the assignment, degree, demand flow and edge flow constraints:
		//Obtain first location and store it in PreviousLocation, and initialize NextLocation
		short int PreviousLocation = CurrentPStep.Locations.front();
		short int NextLocation;
		//Check whether the first location is not the depot
		if (PreviousLocation > 0){
			//Add x to the assignment, degree and demand flow constraints
			Column += CustomerAssignmentConstraints[PreviousLocation - 1](1);
			Column += DegreeConstraints[PreviousLocation - 1](1);
			Column += DemandFlowConstraints[PreviousLocation - 1](CurrentPStep.StartingLoad);
		}
		//Go over all middle locations (i.e., not first and last)
		for (auto CurrentLocation = ++(CurrentPStep.Locations.begin()); CurrentLocation != --(CurrentPStep.Locations.end()); ++CurrentLocation){
			//Iterate NextLocation;
			NextLocation = *CurrentLocation;
			//add x to the assignment and edge flow constraints
			Column += CustomerAssignmentConstraints[NextLocation - 1](2);
			Column += EdgeFlowConstraints[AlgorithmMembers.Arcs[PreviousLocation][NextLocation].Index](1);
			//Update first location
			PreviousLocation = NextLocation;
		}
		//Obtain last location
		short int LastLocation = CurrentPStep.Locations.back();
		//Check whether the last location is not the depot
		if (LastLocation > 0){
			//Add x to the assignment, degree, and demand flow constraints
			Column += CustomerAssignmentConstraints[LastLocation - 1](1);
			Column += DegreeConstraints[LastLocation - 1](-1);
			Column += DemandFlowConstraints[LastLocation - 1](-(CurrentPStep.TotalLoad));
		}
		//Add x to the edge flow constraints
		Column += EdgeFlowConstraints[AlgorithmMembers.Arcs[PreviousLocation][LastLocation].Index](1);

	}
}