#include "Label.h"

Label::Label() {
}

Label::Label(const int& vertexId, const int& length, const int& weight, const double& cost) {
	this->vertexId = vertexId;
	this->length = length;
	this->weight = weight;
	this->cost = cost;
	this->isExtended = false;
}

Label::Label(const Label& l) {
	this->vertexId = l.vertexId;
	this->length = l.length;
	this->weight = l.weight;
	this->cost = l.cost;
	this->ngMemory = l.ngMemory;
	this->active_SSI = l.active_SSI;
	this->sequence = l.sequence;
	this->isExtended = l.isExtended;
}

Label::~Label() {
}

Label& Label::operator=(const Label& l) {
	this->vertexId = l.vertexId;
	this->length = l.length;
	this->weight = l.weight;
	this->cost = l.cost;
	this->ngMemory = l.ngMemory;
	this->active_SSI = l.active_SSI;
	this->sequence = l.sequence;
	this->isExtended = l.isExtended;
	return *this;
}

int operator < (const Label& l1, const Label& l2) {

	//if the cost of l1 is strictly larger than the one of l2, return 0
	if (::strictly_greater(l1.cost, l2.cost)) {
		return 0;
	}

	//here the cost of l1 is less or equal than the one of l2.
	//if the cost of l1 is equal to the cost of l2 AND if the length of l1 is larger or equal than the one of l2, return 0
	if (::greater(l1.cost, l2.cost)) {
		if (l1.length >= l2.length) {
			return 0;
		}
	}

	//here the cost of l1 is less or equal than the one of l2. BUT if it is equal, then the length of l1 is stricly less than the one of l2.
	//if the length of l1 is strictly larger than the one of l2, return 1
	if (l1.length > l2.length) {
		return 1;
	}

	//here the cost of l1 is less or equal than the one of l2 AND the length of l1 is less or equal than the one of l2 AND the number of unreachable nodes of l1 is less or equal than the one of l2.
	//BUT if the costs are equal, then the length of l1 is stricly less than the one of l2.
	//if the unreachable nodes of l1 are node-wise strictly more than those of l2, return 1
	for (int i = 0; i < l1.ngMemory.size(); i++) {
		if ((l1.ngMemory.at(i) & l2.ngMemory.at(i)) != l1.ngMemory.at(i)) {
			return 1;
		}
	}

	//here the cost of l1 is less or equal than the one of l2 AND the length of l1 is less or equal than the one of l2 AND the number of unreachable nodes of l1 is less or equal than the one of l2 AND the unreachable nodes of l1 are node-wise less or equal than those of l2
	//BUT if the costs are equal, then the length of l1 is stricly less than the one of l2.
	return 2;

}

int operator == (const Label& l1, const Label& l2) {

	//Label::comparisonNumber++;

	//if the costs are different, return 0
	if (!::equal(l1.cost, l2.cost)) {
		return 0;
	}

	//if the loads are different, return 0
	if (l1.length != l2.length) {
		return 0;
	}

	//here the costs, the lengths and unreachable node number are equal.
	//if the unreachable nodes are node-wise different, return 1
	for (int i = 0; i < l1.ngMemory.size(); i++) {
		if (l1.ngMemory.at(i) != l2.ngMemory.at(i)) {
			return 1;
		}
	}

	//here the labels are equal
	return 2;

}

int Label::dominates_relaxed(const Label& label) {

	//if the length of this is strictly larger than the one of label, return 0
	if (this->length > label.length) {
		return 0;
	}

	//here the length of this is less or equal than the one of label.
	//if the length of this is equal to the length of label AND if the cost of this is larger or equal than the one of label, return 0
	if (this->length == label.length) {
		if (::greater(this->cost, label.cost)) {
			return 0;
		}
	}

	//here the length of this is less or equal than the one of l2. BUT if it is equal, then the cost of this is stricly less than the one of label.
	//if the vost of this is strictly larger than the one of label, return 1
	if (::strictly_greater(this->cost, label.cost)) {
		return 1;
	}

	//here the cost of l1 is less or equal than the one of l2 AND the length of l1 is less or equal than the one of l2 AND the number of unreachable nodes of l1 is less or equal than the one of l2 AND the unreachable nodes of l1 are node-wise less or equal than those of l2
	//BUT if the costs are equal, then the length of l1 is stricly less than the one of l2.
	return 2;

}

int Label::equal_relaxed(const Label& label) {

	//if the loads are different, return 0
	if (this->length != label.length) {
		return 0;
	}

	//if the costs are different, return 0
	if (!::equal(this->cost, label.cost)) {
		return 0;
	}

	//here the labels are equal
	return 2;

}

int Label::dominates_non_robust(const Label& label, const vector<double>& non_robust_duals, const vector<int>& active_non_robust_cuts) {

	//if the length of this is strictly larger than the one of label, return 0
	if (this->length > label.length) {
		return 0;
	}

	//here the length of this is less or equal than the one of label.
	//if the length of this is equal to the length of label AND if the cost of this is larger or equal than the one of label, return 0
	if (this->length == label.length) {
		if (::greater(this->cost, label.cost)) {
			return 0;
		}
	}

	//here the length of this is less or equal than the one of l2. BUT if it is equal, then the cost of this is stricly less than the one of label.
	//if the vost of this is strictly larger than the one of label, return 1
	if (::strictly_greater(this->cost, label.cost)) {
		return 1;
	}

	//here the length of this is less or equal than the one of label AND the cost of this is less or equal than the one of label.
	//BUT if the lengths are equal, then the cost of this is stricly less than the one of l2.
	//if the unreachable nodes of this are node-wise strictly more than those of label, return 1
	for (int i = 0; i < this->ngMemory.size(); i++) {
		if ((this->ngMemory.at(i) & label.ngMemory.at(i)) != this->ngMemory.at(i)) {
			return 1;
		}
	}

	double red_cost_adjusted = label.cost;
	for (int i = 0; i < active_non_robust_cuts.size(); i++) {
		if (this->active_SSI.at(i) > label.active_SSI.at(i)) {
			red_cost_adjusted -= non_robust_duals.at(i);
			if (::strictly_greater(this->cost, red_cost_adjusted)) {
				return 1;
			}
		}
	}

	//here the cost of l1 is less or equal than the one of l2 AND the length of l1 is less or equal than the one of l2 AND the number of unreachable nodes of l1 is less or equal than the one of l2 AND the unreachable nodes of l1 are node-wise less or equal than those of l2
	//BUT if the costs are equal, then the length of l1 is stricly less than the one of l2.
	return 2;

}

int Label::equal_non_robust(const Label& label, const vector<double>& non_robust_duals, const vector<int>& active_non_robust_cuts) {

	//if the loads are different, return 0
	if (this->length != label.length) {
		return 0;
	}

	//if the costs are different, return 0
	if (!::equal(this->cost, label.cost)) {
		return 0;
	}

	//here the costs, the lengths and unreachable node number are equal.
	//if the unreachable nodes are node-wise different, return 1
	for (int i = 0; i < this->ngMemory.size(); i++) {
		if (this->ngMemory.at(i) != label.ngMemory.at(i)) {
			return 1;
		}
	}

	double red_cost_adjusted = label.cost;
	for (int i = 0; i < active_non_robust_cuts.size(); i++) {
		if (this->active_SSI.at(i) > label.active_SSI.at(i)) {
			red_cost_adjusted -= non_robust_duals.at(i);
		}		
	}
	if (!::equal(this->cost, red_cost_adjusted)) {
		return 1;
	}

	//here the labels are equal
	return 2;

}

int Label::dominates_relaxed_non_robust(const Label& label, const vector<double>& non_robust_duals, const vector<int>& active_non_robust_cuts) {

	//if the length of this is strictly larger than the one of label, return 0
	if (this->length > label.length) {
		return 0;
	}

	//here the length of this is less or equal than the one of label.
	//if the length of this is equal to the length of label AND if the cost of this is larger or equal than the one of label, return 0
	if (this->length == label.length) {
		if (::greater(this->cost, label.cost)) {
			return 0;
		}
	}

	//here the length of this is less or equal than the one of l2. BUT if it is equal, then the cost of this is stricly less than the one of label.
	//if the vost of this is strictly larger than the one of label, return 1
	if (::strictly_greater(this->cost, label.cost)) {
		return 1;
	}

	double red_cost_adjusted = label.cost;
	for (int i = 0; i < active_non_robust_cuts.size(); i++) {
		if (this->active_SSI.at(i) > label.active_SSI.at(i)) {
			red_cost_adjusted -= non_robust_duals.at(i);
			if (::strictly_greater(this->cost, red_cost_adjusted)) {
				return 1;
			}
		}
	}

	//here the cost of l1 is less or equal than the one of l2 AND the length of l1 is less or equal than the one of l2 AND the number of unreachable nodes of l1 is less or equal than the one of l2 AND the unreachable nodes of l1 are node-wise less or equal than those of l2
	//BUT if the costs are equal, then the length of l1 is stricly less than the one of l2.
	return 2;

}

int Label::equal_relaxed_non_robust(const Label& label, const vector<double>& non_robust_duals, const vector<int>& active_non_robust_cuts) {

	//if the loads are different, return 0
	if (this->length != label.length) {
		return 0;
	}

	//if the costs are different, return 0
	if (!::equal(this->cost, label.cost)) {
		return 0;
	}

	double red_cost_adjusted = label.cost;
	for (int i = 0; i < active_non_robust_cuts.size(); i++) {
		if (this->active_SSI.at(i) > label.active_SSI.at(i)) {
			red_cost_adjusted -= non_robust_duals.at(i);
		}
	}
	if (!::equal(this->cost, red_cost_adjusted)) {
		return 1;
	}

	//here the labels are equal
	return 2;

}

void Label::buildSequence(const vector<int>& oldSequence) {
	this->sequence = oldSequence;
	this->sequence.push_back(this->vertexId);
}

void Label::buildNgMemory(const vector<int>& oldNgMemory, const vector<int>& ngSet, const int& word, const int& bitkey) {
	this->ngMemory = vector<int>(oldNgMemory.size());
	for (int i = 0; i < this->ngMemory.size(); i++) {
		this->ngMemory.at(i) = (oldNgMemory.at(i) & ngSet.at(i));
	}
	this->ngMemory.at(word) += bitkey;
	/*this->ngMemory = vector<int>(oldNgMemory.size());
	for (int i = 0; i < this->ngMemory.size(); i++) {
		this->ngMemory.at(i) = (oldNgMemory.at(i));
	}
	this->ngMemory.at(word) += bitkey;*/
}

void Label::buildNgSetFullMemory(const vector<int>& oldNgMemory, const int& word, const int& bitkey) {
	this->ngMemory = oldNgMemory;
	this->ngMemory.at(word) += bitkey;
}

bool Label::isVisited(const int& word, const int& bitkey) const {
	return ((this->ngMemory.at(word) & bitkey) == bitkey);
}

void Label::addNonRobustDual(const double& dual, const int& multiplier, const int& gcd, const int& pos) {
	this->active_SSI.at(pos) += multiplier;
	if (this->active_SSI.at(pos) >= gcd) {
		this->cost += dual;
		this->active_SSI.at(pos) -= gcd;
	}
}

int Label::get_visits_number(const int& vertex_id) const {
	return count(next(this->sequence.begin()), this->sequence.end(), vertex_id);
}

const int& Label::getVertexId() const {
	return this->vertexId;
}
const int& Label::getLength() const {
	return this->length;
}
const int& Label::getWeight() const {
	return this->weight;
}
const double& Label::getCost() const {
	return this->cost;
}
const vector<int>& Label::getNgMemory() const {
	return this->ngMemory;
}
const vector<int>& Label::getSequence() const {
	return this->sequence;
}
const vector<int>& Label::getActive_SSI() const {
	return this->active_SSI;
}
const bool& Label::getIsExtended() const {
	return this->isExtended;
}

void Label::setIsExtended(const bool& res) {
	this->isExtended = res;
}
void Label::setSequence(const vector<int>& seq) {
	this->sequence = seq;
}
void Label::setNgMemory(const int& wordsNumber) {
	this->ngMemory = vector<int>(wordsNumber, 0);
}
void Label::setActive_SSI(const int& number_SSI) {
	this->active_SSI = vector<int>(number_SSI, 0);
}
void Label::setActive_SSI(const vector<int>& active_SSI) {
	this->active_SSI = active_SSI;
}

const string Label::toString() const {
	string str;
	str.append("VERTEX: " + to_string(this->vertexId) + "\tLENGTH: " + to_string(this->length) + "\tWEIGHT: " + to_string(this->weight)
		+ "\tCOST: " + to_string(this->cost) + "\tEXT? " + to_string(this->isExtended) + "\n\tSEQUENCE: ");
	for (int i = 0; i < this->sequence.size(); i++) {
		str.append(to_string(this->sequence.at(i)) + " ");
	}
	str.append("\n\tNG MEMORY: ");
	for (int i = 0; i < this->ngMemory.size(); i++) {
		str.append("(" + to_string(i) + "," + to_string(this->ngMemory.at(i)) + ") ");
	}
	str.append("\n\tACTIVE SSI: ");
	for (int i = 0; i < this->active_SSI.size(); i++) {
		str.append("(" + to_string(i) + "," + to_string(this->active_SSI.at(i)) + ") ");
	}
	str.append("\n");
	return str;
}