void BestEverAspirationCriteria::update(const ISolution& decision)
{
    if (decision.getObjectiveValue() < bestObjectiveValue)
    {
        bestObjectiveValue = decision.getObjectiveValue();
    }
}
bool TSPSolution::operator<(ISolution& s)
{
	if((isFeasible() && s.isFeasible()) || (!isFeasible() && !s.isFeasible()))
	{
		return static_cast<long long>(1000*getObjectiveValue()) < static_cast<long long>(1000*s.getObjectiveValue());
	}
	else
	{
		return isFeasible();
	}
}
bool SimulatedAnnealing::transitionAccepted(IBestSolutionManager& bestSolutionManager, ISolution& currentSolution, ISolution& newSolution, ALNS_Iteration_Status& status)
{
	double temperature = coolingSchedule->getCurrentTemperature();
	if(newSolution < currentSolution)
	{
		return true;
	}
	else
	{
		double difference = newSolution.getPenalizedObjectiveValue() - currentSolution.getPenalizedObjectiveValue();
		double randomVal = static_cast<double>(rand())/static_cast<double>(RAND_MAX);
		return (exp(-1*difference/temperature)>randomVal);
	}
}
std::vector< std::pair<size_t, size_t> > RandomSwapNeighborhood::getSomePairDisk(const ISolution& solution) const
{
    std::vector< std::pair<size_t, size_t> > result;
    std::vector<bool> used(data.getNumberOfDisks() * data.getNumberOfDisks(), false);
    auto distribution = solution.getDistribution();

    srand(time(0));
    for (size_t i = 0; i < numberOfAttempts; ++i)
    {
        size_t diskId1 = 0;
        size_t diskId2 = 0;

        do
        {
            diskId1 = rand() % data.getNumberOfDisks();
            diskId2 = rand() % data.getNumberOfDisks();
        }
        while (distribution[diskId1].serverId == distribution[diskId2].serverId && !used[diskId1 * data.getNumberOfDisks() + diskId2]);

        used[diskId1 * data.getNumberOfDisks() + diskId2] = true;

        result.emplace_back(diskId1, diskId2);
    }

    return result;
}
bool BestEverAspirationCriteria::overrideTabu(const ISolution& solution, const IMove& move) const
{
    if (bestObjectiveValue > solution.tryOnMove(move))
    {
        return true;
    }
    return false;
}
ExponentialCoolingSchedule::ExponentialCoolingSchedule(ISolution& initSol, CoolingSchedule_Parameters& csParam) {
	// The fields are instantiated to default values.
	this->maximumIt = csParam.maxIt;
	this->currentIt = 0;
	this->currentThreshold = 0;
	this->nbThresholds = csParam.nbThresholds;
	this->decreasingFactor = csParam.expPercentageKept;
	this->runTime = csParam.maxRT;
	currentTemperature = (csParam.setupPercentage*initSol.getPenalizedObjectiveValue())/(-log(0.5));
}
std::vector<std::unique_ptr<IMove>> RandomSwapNeighborhood::getMoves(const ISolution& solution) const
{
    auto distribution = solution.getDistribution();
    std::vector< std::unique_ptr<IMove> > result;

    auto swapPairs = getSomePairDisk(solution);

    for (auto pair : swapPairs)
    {
        std::vector<IMove::AtomMove> AtomMoves;
        AtomMoves.emplace_back(distribution[pair.second].serverId, distribution[pair.first].serverId, pair.first);
        AtomMoves.emplace_back(distribution[pair.first].serverId, distribution[pair.second].serverId, pair.second);
        std::unique_ptr<IMove> move(new CompoundMove(std::move(AtomMoves)));
        if (solution.moveIsCorrect(*move))
        {
            result.emplace_back(std::move(move));
        }
    }

    return result;
}
bool DummyAcceptanceModule::transitionAccepted(IBestSolutionManager& bestSolutionManager, ISolution& currentSolution, ISolution& newSolution, ALNS_Iteration_Status& status)
{
	return newSolution.isFeasible();
}
MixLinearCoolingSchedule::MixLinearCoolingSchedule(ISolution& initSol, CoolingSchedule_Parameters& csParam) {
	startTemperature = (csParam.setupPercentage*initSol.getPenalizedObjectiveValue())/(-log(0.5));
	runTime = csParam.maxRT;
	maxIt = csParam.maxIt;
	currentIt = 0;
}