#include "BinaryAntColonyAlgoritm.h"
#include <algorithm>
#include <limits>
#include <functional>
#include <numeric>
#include <iostream>
BinaryAntColonyOptimization::BinaryAntColonyOptimization(std::ostream& outstream, int iteration, int population, double vaporization, double resetThreshold):
	maxIteration(std::max(iteration, 1)), amountOfPopulation(std::max(1, population)), vaporization(std::clamp(vaporization, 0.0, 1.0)), 
	resetThreshold(std::clamp(resetThreshold, 0.0, 1.0)), BinaryHeuristic(outstream)
{

}
Solution BinaryAntColonyOptimization::execute(int rounds, int n, std::function<double(const std::vector<bool>&)> objectiveFunction, ObjectiveFunctionGoal goal)
{
	outstream << "Iteration,populationNumber,objectiveFunction,binary,convergenceFactor" << std::endl;
	std::function<bool(double, double)> op = getOperatorFromGoal(goal);
	Solution bestFound;
	bestFound.objectiveFunctionValue = (goal == ObjectiveFunctionGoal::min) ? std::numeric_limits<double>::max() : std::numeric_limits<double>::min();
	

	for (int r = 0; r < rounds; r++) {
		Solution bestFoundInRun;
		//init with Worst Value
		bestFoundInRun.objectiveFunctionValue = (goal == ObjectiveFunctionGoal::min) ? std::numeric_limits<double>::max() : std::numeric_limits<double>::min();
		//Init Pheromons with 0.5
		std::vector<double> pheromons(n);
		std::fill(pheromons.begin(), pheromons.end(), 0.5);

		std::vector<Solution> population(amountOfPopulation);
		for (int i = 0; i < maxIteration; i++) {
			//Geneartion Population
			for (Solution& sol : population) {
				std::vector<bool> bitstring(n);
				std::transform(pheromons.begin(), pheromons.end(), bitstring.begin(),
					[this](double pheromon)->bool {return rand.doubleRandom() < pheromon; });
				sol.bitstring = bitstring;
				sol.objectiveFunctionValue = objectiveFunction(sol.bitstring);
				//UpdateBest
				if (op(sol.objectiveFunctionValue, bestFoundInRun.objectiveFunctionValue)) {
					bestFoundInRun = sol;
				}
			}
			//Reset
			double convergenceFactor = calculateConvergenceFactor(pheromons);
			if (convergenceFactor >= resetThreshold) {
				std::fill(pheromons.begin(), pheromons.end(), 0.5);
			}
			for (int k = 0; k < population.size() - 1; k++) {
				outstream << r << ","  << i << "," << k << "," << population[k].objectiveFunctionValue << ","
					<< population[k].bitstringToStdString() << "," << convergenceFactor << std::endl;
			}
			//UpdatePheromons
			std::cout << "BestFound:" << bestFoundInRun.bitstringToStdString() << " with value:" << bestFoundInRun.objectiveFunctionValue << " cF:" << convergenceFactor << std::endl;
			for (int bit = 0; bit < n; bit++) {
				bool bestDecision = bestFoundInRun.bitstring[bit];
				pheromons[bit] = (1.0 - vaporization) * pheromons[bit] + (bestDecision ? vaporization : 0.0);
			}

		}
		//update best run
		if (op(bestFoundInRun.objectiveFunctionValue, bestFound.objectiveFunctionValue)) {
			bestFound = bestFoundInRun;
		}
	}
	

	return bestFound;
}






double BinaryAntColonyOptimization::calculateConvergenceFactor(std::vector<double> pheromons)
{
	//Sums the proportion to the marginal values
	double sum = std::transform_reduce(pheromons.begin(), pheromons.end(), 0.0, std::plus<double>(), [](double value) {return std::abs(2*value - 1.0);});
	return sum/(double)pheromons.size();
}
