#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <time.h>
#include <algorithm>		/*	find	*/
#include <vector>			/*	vector	*/
#include <fstream>			/*	to read and write file	*/
#include <string>       	/*	std::string, std::to_string	*/
#include <iterator>  		/*	std::begin, std::end	*/
#include <sstream>
#include <math.h>			/*	sqrt pow	*/
#include <cfloat>			/*	Limits of floating point types	*/

using namespace std;

typedef vector<int> i1d;
typedef vector<i1d> i2d;
typedef vector<double> d1d;
typedef vector<d1d> d2d;
typedef vector<bool> b1d;
typedef vector<b1d> b2d;

i1d shortest_tour;
i2d ant_position;
b2d tabu;
d2d trail;
d2d delta_trail;
d2d distanceXY;
d2d locationXY;
d2d transportation_probability;
i1d unused_position;

int edge_weight_type;
int dimension;
int sum_ant;
int Nc = 0;		//NC is the cycles counter
int NcMax;
int S;

double alpha;
double beta;
double rho;
double tau;
double Q;
double shortest_distance = DBL_MAX;

string dataset_name;

void 	init();
int 	random(int low, int up=0);
double 	random(double low, double up=0.0);
void	read_file(string filename);
void	write_file(string ,string);	//[filename, context]
void	file_context_analyse(string, int);
double	Euclidean_distance(d1d, d1d);
void	caculate_distance();
void	run();
double	get_distance(int, int);
double	get_trail(int, int);
void 	ant_system();
void 	first_trip();
void	delta_trail_update(int, int, double);
void	trail_update_and_reset_delta_trail();
void	unused_position_reset();
void	unused_position_used(int);
double	caculate_transition_probability(int, int, int);

int main(int argc, char* argv[])
{
	srand( (unsigned)time(NULL) );
	
	NcMax = 100;
	tau=0.0;
	Q=100.0;					// not sure what data to set
	alpha = 0.1;
	beta = 2;
	rho = 0.9;
	
	edge_weight_type = 2;
	dimension = 51;	dataset_name = "tsp51.txt";
	sum_ant = dimension;
	
	run();

	cout << "End of the Ant optimization." << endl;
	system("pause");
	return 0;
}

void run(){
	init();
	read_file(dataset_name);
	caculate_distance();
	ant_system();
}

void init(){
	ant_position.assign(sum_ant, i1d(dimension+1, 0));			/*need go to start at end of the trip*/
	shortest_tour.assign(dimension+1, 0);
	tabu.assign(sum_ant, b1d(dimension, true));
	trail.assign(dimension, d1d(dimension, tau));				/*	trail[i][j]	j>=i	*/
	delta_trail.assign(dimension, d1d(dimension, 0.0));			/*	delta_trail[i][j]	j>=i	*/
	locationXY.assign(dimension, d1d(edge_weight_type, 0.0));
	distanceXY.assign(dimension, d1d(dimension, 0.0));			/*	distanceXY[i][j]	j>=i*/
	transportation_probability.assign(sum_ant, d1d(dimension, 0.0));
	unused_position.assign(dimension, 0);
	for(int i=0; i<dimension; i++)	unused_position[i] = i;
}

void ant_system(){
	first_trip();
	
	do{
		trail_update_and_reset_delta_trail();
		Nc++;
		
		unused_position_reset();
		
		int this_position , next_position;
		
		for(int k=0; k<sum_ant; k++){		//k is index of ant
			unused_position_reset();
			this_position = random(dimension-1);
			
			ant_position[k][0] = this_position;
			unused_position_used(this_position);
			
			for(int s=1; s<dimension; s++){			//s is index of tabu, is number of cities
				double q;
				do{
					//if q=1.0 , r will be something wrong
					q = random(1.0);
				}while(q==1.0);
				int i = 0;
				double r = caculate_transition_probability(this_position, unused_position[i], k);;
				
				while( r<q && i<unused_position.size() ){
					i++;
					double p = caculate_transition_probability(this_position, unused_position[i], k);
					r += p;
				}
				next_position = unused_position[i];
				
				unused_position_used(next_position);
				ant_position[k][s] = next_position;
				this_position = next_position;
			}
		}
	}while(Nc<NcMax);
	
}

void first_trip(){
	int next_position;
	/*Place the k ants on the s nodes, first time all ants walk randomly without repeat city*/
	for(int k=0; k<sum_ant; k++){		/*k is index of ant*/
		unused_position_reset();
		for(int s=0; s<dimension; s++){			/*s is index of tabu, is number of cities*/
			next_position = unused_position[ random( (int)unused_position.size() - 1 ) ];
			unused_position_used(next_position);
			
			ant_position[k][s] = next_position;
		}
	}
	Nc++;
}

void trail_update_and_reset_delta_trail(){
	for(int k=0; k<sum_ant; k++){								/*k is the index of ant*/
		ant_position[k][dimension] = ant_position[k][0];		/*set last position equal to first position*/
		
		double distance=0.0;
		
		for(int s=0; s<dimension; s++)
			distance += get_distance(ant_position[k][s], ant_position[k][s+1]);
		for(int s=0; s<dimension; s++){
			delta_trail_update(ant_position[k][s], ant_position[k][s+1], distance);
		}
		
 		if(shortest_distance>distance){
			shortest_distance = distance;
			shortest_tour.assign(ant_position[k].begin(), ant_position[k].end());
			cout << "NO." << Nc << " ant= "<< k << "\tshortest_distance = " << shortest_distance << endl;
			
			/* This shown the optimal position  
			for(int i=0; i<dimension; i++){
				cout << ant_position[k][i] << " ";
			}
			cout << endl;
			/* End of shown the optimal position  */
		}
	}
	
	//
	for(int i=0; i<distanceXY.size(); i++){
		for(int j=i; j<distanceXY.size(); j++){
			trail[i][j]  = trail[i][j]*rho + delta_trail[i][j];
			delta_trail[i][j] = 0.0;				
		}
	}
}

double caculate_transition_probability(int this_position, int next_position, int k){
	double p, q=0.0;
	
	p  = pow( get_trail(this_position, next_position), alpha );
	p *= pow( 1/get_distance(this_position, next_position), beta );
	for(int j=0; j<(int)unused_position.size(); j++){
		q += ( pow( get_trail(this_position, unused_position[j]), alpha ) * pow( 1/get_distance(this_position, unused_position[j]), beta ) );
	}
	return p/q;
}

void delta_trail_update(int i, int j, double d){
	if(i>j)	delta_trail[j][i] += Q/d;
	else	delta_trail[i][j] += Q/d;
}

void unused_position_reset(){
	unused_position.assign(dimension, 0);
	for(int i=0; i<dimension; i++)	unused_position[i] = i;
}

void unused_position_used(int position){
	vector<int>::iterator pos;
	pos = find(unused_position.begin(), unused_position.end(), position);
	unused_position.erase(pos);
}

int random(int low, int up){						/*	low<=random(low,up)<=up	*/
	if(low>up)	swap(low,up);						/*		0<=random(a)<=a		*/
	return rand() % (up - low + 1) + low ;
}

double random(double low, double up){ 				/* low<=random(low,up)<=up	*/
	if(up<low)	swap(low,up);						/*		0<=random(a)<=a		*/
	return (up - low) * rand() / (RAND_MAX ) + low;
}

void read_file(string filename){
//	http://w...content-available-to-author-only...s.com/doc/tutorial/files/
	int count = 0;
	string line;
	ifstream myfile (filename.c_str());
	if (myfile.is_open()){
		while ( getline (myfile,line) ){
			file_context_analyse(line, count);
			count++;
		}
		myfile.close();
	}else{
		cout << "Read file error. Please check again." << endl;
		exit(1);
	}
}

void write_file(string filename, string context){
	ofstream myfile (filename.c_str());
	if (myfile.is_open()){
		myfile << context;
		myfile.close();
	}else {
		cout << "Unable to open file" << endl;
		exit(1);
	}
}

void file_context_analyse(string str, int count){
	istringstream ss(str);
    istream_iterator<double> begin(ss), end;
	
    //putting all the tokens in the vector
    d1d arrayTokens(begin, end);
	
	/*check the dimension is as same as file*/
    if(edge_weight_type!=arrayTokens.size()){
    	cout << "Dimension setting error, Please check again." << endl;
    	exit(1);
	}
	
    /*Save context to locationXY*/
    for(int i=0; i<arrayTokens.size(); i++){
    	locationXY[count][i] = arrayTokens[i];
	}
}

double Euclidean_distance(d1d location1, d1d location2){
	//https://e...content-available-to-author-only...a.org/wiki/Euclidean_distance
	double d=0.0;
	for(int i=0; i<location1.size(); i++){
		d += (pow(location1[i]-location2[i],2));
	}
	return sqrt(d);
}

void caculate_distance(){
	for(int i=0; i<locationXY.size(); i++){
		for(int j=i; j<locationXY.size(); j++){
			if(i==j){
				distanceXY[i][j] = 0;
			}else{
				distanceXY[i][j] = Euclidean_distance(locationXY[i], locationXY[j]);
			}
		}
	}
}

double get_distance(int i, int j){
	if(i>j)	/*swap(i, j);*/ return( distanceXY[j][i] );
	else return( distanceXY[i][j] );
}

double get_trail(int i, int j){
	if(i>j)	/*swap(i, j);*/ return( trail[j][i] );
	else return( trail[i][j] );
}
