#include <iostream>
#include <cstdlib>
#include <conio.h>
#include <math.h>
#include <ctime>
#include <fstream>

using namespace std;

struct X 
{
  int task, gad, rannum;      
       };

int random(int bb, int tb)
{
    return bb+rand()%(tb-bb);
}    


class Individ
{
   private:   
   X *vector;
   int criterion, sv;
   
   public:
   Individ(){} // constructor without parametres
   Individ(int sv_1)// constructor with parametres
     { 
       sv = sv_1;
       vector = new X[sv];
       for(int i = 0; i < sv; i++)
       {
         vector[i].task = 0; vector[i].gad = 0; vector[i].rannum = 0;
       }         
     }
     
   Individ (const Individ& Individ_1) // copy-constructor 
     {
       sv = Individ_1.sv;
       vector = new X[sv];
       for(int i = 0; i < sv; i++)
       {
        vector[i].task = Individ_1.vector[i].task;
        vector[i].gad = Individ_1.vector[i].gad;
        vector[i].rannum = Individ_1.vector[i].rannum;
       }            
       criterion=Individ_1.criterion;
       }
      
   Individ operator = (Individ Individ_1) //overload "="
     {
       sv = Individ_1.sv;
       vector = new X[sv];
       for(int i = 0; i < sv; i++)
         {
           vector[i].task = Individ_1.vector[i].task;
           vector[i].gad = Individ_1.vector[i].gad;
           vector[i].rannum = Individ_1.vector[i].rannum;
         }            
         criterion = Individ_1.criterion;
         return *this;
     }
     
   
Individ CrossOver(Individ Individ_1, int sv_1, ofstream& out)// operation of crossover
{   
            Individ xxx = Individ_1.sv;
            int EP;       
            int *vec;
            static int one = 1;
            
            vec = new int [sv_1];
            
            for(int i = 0; i < sv_1; i++) vec[i]=0;


            srand(time(0)+random(0,sv));
            EP = random(1,sv);
            
            cout <<"Exchange point: " <<EP<<endl;
            out <<"Exchange point: " <<EP<<endl;
            
            for(int i = 0; i < sv; i++) xxx.vector[i].task = vector[i].task;

            for(int i = EP; i < sv; i++)
            {
                xxx.vector[i].rannum = Individ_1.vector[i].rannum;
                xxx.vector[i].gad = Individ_1.vector[i].gad;
            }
            
            for(int i = 0; i < EP; i++)
            {
                xxx.vector[i].rannum = vector[i].rannum;
                xxx.vector[i].gad = vector[i].gad;
            }

          
            for(int i = 0; i < sv; i++)
            {
                for(int j = 1; j <= sv_1 ; j++)
                {
                    if (xxx.vector[i].gad == j)
                    { 
                        vec[j-1] = vec[j-1]+xxx.vector[i].task;
                        j=1;
                        break;
                    }
                }
            }
            
            int qos;
            for(int i = 0; i < sv_1; i++)
            {
                for(int j = sv_1-1; j > i; j--)
                {
                    if( vec[j-1] > vec[j])
                    {
                       qos = vec[j-1];
                       vec[j-1] = vec[j];
                       vec[j] = qos; 
                    }
                }
            }
            one=one*2;
            xxx.criterion = vec[sv_1-1];
            return xxx;
        }
      
      
          

 Individ Mutation(int sv_1, ofstream& out)// operation of mutation
 {
            int mutelem, ranbit, sum=0,a, EP;
            Individ xxx(*this);
            int v[8];
            int *vec;
            static int one = 1;

            vec = new int [sv_1];
            
            for(int i = 0; i < sv_1; i++)
             vec[i] = 0;
            for(int i = 0; i < 8; i++)
             v[i]=0;
            

            srand(time(0)+random(1,sv_1));
            mutelem = random(1,sv);
            
            cout<<"Operation of a mutation with an element:" <<mutelem<<endl;
            out<<"Operation of a mutation with an element: " <<mutelem<<endl;
            
            a = xxx.vector[mutelem-1].rannum;
            
           int i=7;
            while(a!=0)
            {
                v[i] = a%2;
                a = a/2;
                i--;
            }
            
      

            srand(time(0)+random(1,sv));
            ranbit = random(0,7);
            
            cout<<"Bit: " <<ranbit<<endl;
            out<<"Bit: " <<ranbit<<endl;
            if (v[7-ranbit] == 0) v[7-ranbit] = 1;
            else v[7-ranbit] = 0;

         
            for(i = 0; i < 8; i++) sum=sum+v[i]*(int)pow(2,7-i);

            xxx.vector[mutelem-1].rannum = sum;
            cout<<"New element after mutation: "<<xxx.vector[mutelem-1].rannum<<endl;
            out<<"New element after mutation: "<<xxx.vector[mutelem-1].rannum<<endl;
            
            for(i = 0; i < xxx.sv;i++)
            {
                for(int j = 1; j <= sv_1;j++)
                {
               	    if(xxx.vector[i].rannum <= 257/((double)sv_1/j))
                    {
                        xxx.vector[i].gad = j;
                        j=1;
                        break;
                    }	
                }
            }
        
            for(i = 0; i < xxx.sv; i++)
            {
                for(int j = 1; j <= sv_1; j++)
                {
                    if(xxx.vector[i].gad == j)
                    { 
                        vec[j-1] = vec[j-1]+xxx.vector[i].task;
                        j=1;
                        break;
                    }
                }
            }
           
            int qos;
            for(i = 0; i < sv_1; i++)
            {
                for(int j = sv_1-1; j > i;j--)
                {
                    if(vec[j-1] > vec[j])
                    {
                       qos = vec[j-1];
                       vec[j-1] = vec[j];
                       vec[j] = qos; 
                    }
                }
            }
            one = one*2;
            xxx.criterion = vec[sv_1-1];
            return xxx;
        }
        
 void Initialization(int **Ma3x_1, int sv_2, int ran, int sv_1)//create generation
 {
            int *vec;

            vec = new int[sv_1];
            for(int i = 0; i < sv_1; i++)
                vec[i] = 0;

            sv = sv_2;
            vector = new X [sv_2];
            
            for(int i = 0; i < sv; i++)
                vector[i].task = Ma3x_1[i][0];
            
            srand(time(0)+random(0,ran));
            for(int i = 0; i < sv; i++)
                vector[i].rannum = random(0,256);
            
            for(int i = 0; i < sv; i++)
            {
                for(int j = 1; j <= sv_1; j++)
                {
               	    if(vector[i].rannum <= 257/((double)sv_1/j))
                    {
                        vector[i].gad = j;
                        j=1;
                        break;
                    }	
                }
            }
            
            for(int i = 0; i < sv; i++)
            {
                for(int j = 1; j <= sv_1; j++)
                {
                    if( vector[i].gad == j)
                    { 
                        vec[j-1] = vec[j-1]+vector[i].task;
                        j=1;
                        break;
                    }
                }
            }
            
            int qos;
            for(int i = 0; i < sv_1; i++)
            {
                for(int j = sv_1-1; j > i; j--)
                {
                    if(vec[j-1] > vec[j])
                    {
                       qos = vec[j-1];
                       vec[j-1] = vec[j];
                       vec[j] = qos; 
                    }
                }
            }
            criterion = vec[sv_1-1];
        }
        
int get_Criterion_T()
        { return criterion; }
        
 
void ToDisplay(ofstream& out)// show generation & individual
    {
            for(int i = 0; i < sv; i++)
            {
                cout<<vector[i].task<<' ';
                out<<vector[i].task<<' ';
            }
            cout <<endl;
            out <<"\n";

            for(int i = 0; i < sv; i++)
            {
                cout<<vector[i].rannum<<' ';
                out<<vector[i].rannum<<' ';
            }
            cout <<endl;
            out <<"\n";

            for(int i = 0; i < sv;i++)
            {
                cout<<vector[i].gad<<' ';
                out<<vector[i].gad<<' ';
            }
            cout<<endl; 
            out<<"\n";
            out<<"Criterion T=" <<criterion <<' ';
            out<<"\n";
            cout<<"Criterion T= " <<criterion <<' ';
   
     }
     
      ~Individ()// destructor
        {
            delete vector;
        }
     
};

int Criterion_T(Individ *Individ_1, int sv)
 {
    int *vec;
    int qos;

    vec = new int[sv];
    for(int i = 0; i < sv; i++)
        vec[i] = Individ_1[i].get_Criterion_T();
 
    for(int i = 0; i < sv; i++)
    {
        for(int j = sv-1; j > i; j--)
        {
            if(vec[j-1] > vec[j])
            {
                qos = vec[j-1];
                vec[j-1] = vec[j];
                vec[j] = qos; 
            }
        }
    }
    return vec[0];
}


int main()
{ 
    int nog, nota, bb, tb, num, noi, nore, one = 1, nore_1, procro, promut, y,ww, i=0 ;
    int **Ma3x;
    ofstream out("SaveGA.txt",ios::out);
    
         cout<<"Please, enter the number of gadget! "<<endl;   
         cin>>nog;
         out<<"The number os gadget: "<<nog<<endl; 
         cout<<"Please, enter the number of tasks! "<<endl;
         cin>>nota;
         out<<"The number os tasks: "<<nota<<endl; 
         cout<<"Please, enter the bottom border! "<<endl;
         cin>>bb;
         out<<"The bottom border: "<<bb<<endl; 
         cout<<"Please, enter the top border! "<<endl;
         cin>>tb;
         out<<"The top border: "<<tb<<endl<<endl;; 
         
         
         Ma3x = new int*[nota];
           for(int i = 0; i < nota;i++) Ma3x[i] = new int[nog];
    
    srand(time(0)+bb+tb);
    for(int i = 0; i < nota; i++)
    {   
        num = bb+rand()%(tb - bb);
        for(int j = 0; j < nota; j++) Ma3x[i][j] = num;
    }
    
    cout<<"Distribution tasks of the gadget: "<<endl;
    out<<"Distribution tasks of the gadget: "<<endl;
    
    for(int i = 0; i < nota; i++)
    {
        for(int j = 0; j < nog; j++)
        {
            cout <<Ma3x[i][j] <<' ';
            out <<Ma3x[i][j] <<' ';
        }
        out <<endl;
        cout <<endl;
    }

    cout <<endl;
  
    Individ** Individ_1;
    Individ vv = nog;
    
    cout<<"Please, enter number of Individ: "<<endl;
    cin>>noi;
    out <<"Number of Individ: " <<noi<<endl;
    
    cout <<"Please, enter number of Repetition: "<<endl;
    cin >>nore;
    out <<"Number of Repetition: " <<nore<<endl;
    nore_1 = nore;

    Individ_1 = new Individ *[2];
    for(int i = 0; i < 2; i++)
        Individ_1[i] = new Individ[noi];

   
    for(int j = 0; j < noi; j++)//create "0"-gen
        Individ_1[0][j].Initialization(Ma3x, nota, j+234, nog);

    for(int i = 0; i < nota; i++)
        delete[] Ma3x[i];
    delete[] Ma3x;

    out <<" '0'-generation: " <<"\n";
    cout <<" '0'-generation: " <<endl;
    for(int j = 0; j < noi; j++)
    {
        cout <<"Individ " <<j <<endl;
        out <<"Individ " <<j <<endl;
        Individ_1[0][j].ToDisplay(out);
        cout<<endl<<endl;
        out<<endl<<endl;
    }
   
   
 

    while( one != nore)
    {
        for(int j = 0; j < noi; j++)
        {
            srand(time(0)+random(0,nore));
            procro=random(0,100);
            
            if( procro <= 90) 
            {
                srand(time(0)+random(1,procro));
                y = random(0,noi);
                while(y == j)  y = random(0,noi); 
                
                cout <<"CrossOver: Individ" <<i <<',' <<j <<" Individ" <<i <<',' <<y  <<endl;
                out <<"CrossOver: Individ" <<i <<',' <<j <<" Individ" <<i <<',' <<y  <<endl;
                
                vv = Individ_1[0][j].CrossOver(Individ_1[0][y], nog, out);
                cout <<endl;
                out <<endl;
                vv.ToDisplay(out);
                out <<endl <<endl;
                cout <<endl <<endl;
            }
            srand(time(0)+random(1,nore));
            promut=random(0,100);
            if(promut >= 90)
            {   
                out <<endl <<"USED MUTAGEN!!!" <<endl;
                cout <<endl <<"USED MUTAGEN!!!" <<endl;
                vv = vv.Mutation(nog, out);                
                out <<endl;
                cout <<endl;
                vv.ToDisplay(out);
                out <<endl;
                cout <<endl;
            }
            srand(time(0)/RAND_MAX+random(1,nog));
            ww = 0+rand()%noi;
            out <<"Comparison with Individ" <<i <<',' <<ww<<endl;
            cout <<"Comparison with Individ" <<i <<',' <<ww<<endl;
            if(vv.get_Criterion_T() <= Individ_1[0][ww].get_Criterion_T()) Individ_1[1][j] = vv;
            else Individ_1[1][j] = Individ_1[0][ww];
        }
        if(Criterion_T(Individ_1[0],noi) == Criterion_T(Individ_1[1],noi)) one = one + 1;
        if(Criterion_T(Individ_1[0],noi) != Criterion_T(Individ_1[1],noi)) one = 1;

        for(int k = 0; k < noi; k++)
            Individ_1[0][k] = Individ_1[1][k];

        cout <<"***GENERATION*** "<<i+1<<endl;
        out <<"***GENERATION*** " <<i+1<<endl;
        for(int k = 0; k<noi; k++)
        {
            cout <<"__Individ__ " <<k <<endl;
            out <<"__Individ__ " <<k <<endl;
            Individ_1[0][k].ToDisplay(out);
            cout <<endl <<endl;
            out <<endl <<endl;
        }
        i++;
    }
    
    out.close();

    getch();    
}