#include <iostream>
#include<conio.h>
#include <stdio.h>
#include <iostream>
#include <stdlib.h>
#include <windows.h>
using namespace std;



struct Individ
{
  int task [100], gad [100], rannum [100], crit_T;     
};

struct Individ vec[100];
struct Individ vec_next_gen[100];
struct Individ X;
struct Individ Elite;

int bb, tb, noI, noTA, noG, noRE, si, count_1, count_rep;


int To_Get_NoG (int rannum,int noG)
{ int x = 0, y = 0, z = 0;
      
  x = abs(256/noG);
       
  for (int i = 0; i <= noG; i++)
  {  
      z = y; y = y + x;
      if ( (rannum >= z) && (rannum <= y)) return i+1;
  }
}

int To_Set_Crit_T(Individ Ind)
{   
    int vec[noG], T = 0;
    
    for (int i = 0; i < noG; i++)
    vec[i] = 0;
    
    for (int j = 0; j < noG; j++)
    {
       for (int k = 0; k < noTA; k++)
       if (Ind.gad[k] == j+1) vec[j] = vec[j] + Ind.task[k];
    } 
    T = vec[0];
    
    for (int l = 1; l < noG; l++)
        if (vec[l] > T) T = vec[l];
   return T;
}

int To_Get_T_Max(Individ *Ind)
{   int T_Max;
	T_Max = Ind[0].crit_T;
	for(int i = 0; i < noI; i++)
     if ( Ind[i].crit_T < T_Max) T_Max = Ind[i].crit_T;
	return T_Max;
}

void To_Display( Individ *Ind)
{   
   cout<<"________________________________________________________________________________"<<endl;
   
    cout<<"_ELITE_ "<<endl<<endl;
    
     for(int i = 0; i < noTA; i++)
     {
      cout<<Ind[0].task[i];
      cout<<"     ";
     }
       cout<<endl;	
       	
		for(int i = 0; i < noTA; i++) 
         {
          cout<<Ind[0].rannum[i];
          cout<<"   ";
          }
         cout<<endl;
        for(int i = 0; i < noTA; i++) 
        
          cout<<"{"<<Ind[0].gad[i]<<"}"<<"   ";
        
         
          Ind[0].crit_T = To_Set_Crit_T(Ind[0]);     
          cout<<endl<<endl;           
		  cout<<"Criterion_T = "<<Ind[0].crit_T;
          cout<<" ";
           cout<<endl<<endl;;
   
    for (int j = 1; j < noI; j++)
	{
          
       
     cout<<"_INDIVID_ "<<j<<endl<<endl;
     
     for(int i = 0; i < noTA; i++)
     {
      cout<<Ind[0].task[i];
      cout<<"     ";
     }
       cout<<endl;		
       
		for( int k = 0; k < noTA; k++)
        cout<<Ind[j].rannum[k]<<"    ";
      
         cout<<endl;
      	
       for( int l = 0; l < noTA; l++)
       {
          
            cout<<"{";
            cout<<Ind[j].gad[l]<<"}"<<"   ";
       }
        
    cout<<endl<<endl; 
    
    Ind[j].crit_T = To_Set_Crit_T(Ind[j]);                
	cout<<"Criterion_T = "<<Ind[j].crit_T;
    cout<<" ";
    cout<<endl<<endl;

	}
    cout<<endl<<endl;    
    cout<<"T_minimax = "<<To_Get_T_Max(Ind)<<endl;
    cout<<"________________________________________________________________________________"<<endl;
 }   
     

void To_SortX(int *mas)
{
  int x;
  long i , j;
  for ( i = 0;  i < noTA; i++)               
   { 
    x = mas[i];
		
    for ( j = i - 1; (j >= 0) && (mas[j] < x); j--)
      mas[j+1] = mas[j]; 
      mas[j+1] = x;
   }  
}

void To_Create_Elite()
{
     int v_1[noTA], v_2[noTA], v_3[noTA], v_m[noG];
     bool temp[noTA], key = 0;
     int q,p;
     for ( int i = 0; i < noTA; i++)
     {
     v_1[i] = 0;
     v_2[i] = 0;
     v_3[i] = 0;
     temp[i] = 0;
     }
     
     for (int j = 0; j < noG; j++)
     v_m[j]=0;
     
     for (int k = 0; k < noTA; k++)
      v_1[k] = Elite.task[k];
      
     To_SortX(v_1);
     
     for (int i = 0; i < noTA; i++)
     {  
        q = 0;
        for(int j = 1; j < noG; j++)
        if (v_m[j] < v_m[q]) q = j;
        v_m[q] = v_m[q] + v_1[i];
        v_3[i] = q + 1;
        v_2[i] = (v_3[i] - 1) * abs(256/noG) + abs(256/noG)/2;        
     
     }
         
         p = 0;
         for (int i = 1; i < noG; i++)
         if ( v_m[i] > v_m[p]) p = i; Elite.crit_T = v_m[p];
                 
        for (int j = 0; j < noTA; j++)
         {
          for (int k = 0; k < noTA; k++)
           if ((Elite.task[j] == v_1[k]) && (key == 0))
           {
            Elite.gad[j] = v_3[k];
            Elite.rannum[j] = v_2[k];
            key = 1;
            v_1[k] = 0;
           }
            key = 0;
}
}

void To_Find_Elite()
{
     int p = 0;
     int T_Max;
     T_Max = vec_next_gen[0].crit_T;
     
     for(int i = 1; i < noI; i++)
      vec_next_gen[i].crit_T = To_Set_Crit_T(vec_next_gen[i]);
      
     for(int j = 1; j < noI; j++)
      if (vec_next_gen[j].crit_T < T_Max)
       {
        T_Max = vec_next_gen[j].crit_T;
        p = j;
       }
     if ( p != 0)
     {
      Elite = vec_next_gen[0];
      vec_next_gen[0] = vec_next_gen[p];
      vec_next_gen[p] = Elite;
     }
     
      cout<<p<<"-Individ became the Elite Individ"<<endl;
     
}

void To_Create_0_Gen()
{
  int key = 0, temp;
   for (int i = 0; i < noTA; i++)
    {
       while(key == 0)
        {
          temp = rand()%(tb);
          if (temp > bb) 
          {
           key = 1;
           Elite.task[i] = temp;         
           for (int j = 0; j < noI; j++ )
           {
            vec[j].task[i] = temp;
            vec_next_gen[j].task[i] = vec[j].task[i]; 
            vec[j].rannum[i] = rand()%(255)+1;
            vec_next_gen[j].rannum[i] = vec[j].rannum[i]; 
		    vec[j].gad[i] = To_Get_NoG(vec[j].rannum[i], noG);
            vec_next_gen[j].gad[i] = vec[j].gad[i];    
           }
                   
         }            
    }
    key = 0;
    }
    To_Create_Elite();
    vec[0] = Elite;
    vec_next_gen[0] = Elite;
}

void CrossOver(int i)
{
 int procro,EP,x;
 
	do
    x = rand()%(noI);
    while (x == i);
    
	procro = rand()%(100);
	if(procro<90)
	{
     
     cout<<endl;           
     cout <<"CrossOver: " << i <<"-Individ with " << x <<"-Individ "  <<endl;
     cout <<endl;
     
    
	EP = rand()%(noTA-1);
    cout <<"Exchange point: " <<EP<<endl;
    cout <<endl;
    
    for(int j = EP + 1; j <= noTA; j++)
    { 
     X.task[j] = vec[x].task[j]; 
     X.rannum[j] = vec[x].rannum[j];
     X.gad[j] = vec[x].gad[j];
    }
    
    cout<<"New element after CrossOver: "<<endl;
    
    for(int k = 0; k < noTA; k++)
    {
     cout<<X.rannum[k];
     cout<<"{"<<X.gad[k];
     cout<<"} ";
    }
    
    X.crit_T = To_Set_Crit_T(X);   
    cout<<"Criterion_T = "<<X.crit_T;
    cout<<" ";
    cout<<endl;	
	}
    
     
 }
     
void Mutation(int i)
{
 int promut, ranbit, temp, temp_rn_1, temp_rn_2;
  promut = rand()%(100);
  if (promut >= 90)
  { 
    cout <<endl <<"USED MUTAGEN!!!" <<endl;
    cout <<endl;
             
	cout<<"Operation of a mutation with an element: " <<i<<endl;
    cout <<endl;
    
		ranbit = rand()%(8);
	cout<<"Bit: "<<ranbit<<endl;
		temp = rand()%(noTA);
		temp_rn_1 = X.rannum[temp];
		temp_rn_2 = X.rannum[temp];
		temp_rn_1 = ~((~temp_rn_1)&(~(1<<ranbit)));
		temp_rn_2 = ~((~temp_rn_2)|(1<<ranbit));
		
		if(temp_rn_1 == X.rannum[temp])
        {X.rannum[temp] = temp_rn_2;}
        
		else
        {X.rannum[temp] = temp_rn_1;}
        
	for(int i = 0; i < noTA; i++)
	{
		X.gad[i] = To_Get_NoG(X.rannum[i], noG);
    }
    
		cout<<"New element after CrossOver: ";
		
         for(int j = 0; j < noTA; j++)
         {
          cout<<X.rannum[j];
          cout<<"{"<<X.gad[j];
          cout<<"} ";
         }
         
    X.crit_T = To_Set_Crit_T(X);   
    cout<<"Criterion_T = "<<X.crit_T;
    cout<<" ";
    cout<<endl;	
	}
     
 }     
     
void Tournament_Selection()
{
 int temp;
 do
 temp = rand()%(noI);
 while (temp == si);
    cout<<endl;
	cout<<"Tournament Selection : ";
	cout <<"Comparison "<<si<<" - Individ"<<" with "<<temp<<"- Individ"<<endl;
    cout <<endl;

	if (X.crit_T <= vec[temp].crit_T)
 	{
 	for (int i = 0; i < noTA; i++)
	{
	 vec_next_gen[si].task[i] = X.task[i];
	 vec_next_gen[si].rannum[i] = X.rannum[i];
	 vec_next_gen[si].gad[i] = X.gad[i];
	}
	
 	cout<<"New Individ copy to NextGen"<<endl;;
    }
 	
	else
		{
		for (int j = 0; j < noTA; j++)
		{
		vec_next_gen[si].task[j] = vec[temp].task[j];
		vec_next_gen[si].rannum[j] = vec[temp].rannum[j];
		vec_next_gen[si].gad[j] = vec[temp].gad[j];
		}
	cout<<"Old Individ copy to NextGen"<<endl;
     
     }    
 }     
void To_CheckOfTermAlg()
{
   count_1++;
 cout<<"                                             "<< To_Get_T_Max(vec_next_gen)<<endl;
 cout<<"                                             "<< To_Get_T_Max(vec)<<endl;
 
 if (To_Get_T_Max(vec_next_gen) == To_Get_T_Max(vec))
	count_rep++;
     else
      count_rep=0;
      
cout<<"Count of Repetition = "<<count_rep<<endl;

if (count_rep >= noRE)
 { 
    cout<<endl;          
    cout<<"The Algorythm is finished. Criterion Tmax has repeated "<<noRE<<" times!!! "<<endl;
    
     getch();
     exit(1);}  
     
}      

void To_Create_Next_Gen()
{
	for (int i = 0; i < noI; i++)
	{
        si = i;
        
     for(int j = 0; j < noTA; j++)
	{
		X.task[j] = vec[i].task[j];
		X.rannum[j] = vec[i].rannum[j];
		X.gad[j] = vec[i].gad[j];
		X.crit_T = vec[i].crit_T;
	}  
	    
	    
        cout<<endl;
		CrossOver(i);
		Mutation(i);
        Tournament_Selection();
   }
    To_Find_Elite();
    To_Display(vec_next_gen);
    To_CheckOfTermAlg();
    
    for (int k = 0; k < noI; k++)
	{
    	for (int l = 0; l < noTA; l++)
		{
	    	vec[k].task[l] = vec_next_gen[k].task[l];
		    vec[k].rannum[l] = vec_next_gen[k].rannum[l];
            vec[k].gad[l] = To_Get_NoG(vec[k].rannum[l], noG);
		}	 
      vec[k].crit_T = To_Set_Crit_T(vec[k]);
	}
}




void Message()
{
    
    cout<<endl<<endl; 
    
    cout<<"1. To Show the Generation "<<endl;
    cout<<" 2. To Create NextGeneration "<<endl;
    cout<<"  3. To Clean screen "<<endl;
    cout<<"Enter 'ESC' for Exit!"<<endl;
}

int main()
{
    srand(time(NULL));
    int ch;
         cout<<"Please, enter the number of gadget! "<<endl;   
         cin>>noG;
          
         cout<<"Please, enter the number of tasks! "<<endl;
         cin>>noTA;
          
         cout<<"Please, enter the bottom border! "<<endl;
         cin>>bb;
          
         cout<<"Please, enter the top border! "<<endl;
         cin>>tb;

         cout<<"Please, enter number of Individ: "<<endl;
         cin>>noI;
   
         cout <<"Please, enter number of Repetition: "<<endl;
         cin >>noRE;
         
         cout<<endl;
         cout <<endl;
         cout <<"*** 0 - GENERATION : ***" <<endl;
         cout <<endl;
         To_Create_0_Gen();
         To_Find_Elite();
         To_Display(vec_next_gen);
         
         while(ch != 27) 
         {
           Message();       
           ch = getch();
            
           switch(ch)
           {
             case 49: To_Display(vec); break;
             case 50: To_Create_Next_Gen(); break;
             case 51: system("cls"); break;     
                
           }          
   }
         
         
         
         
}