/*************************************************************************
* Project: Library of Evolutionary Algoriths
*************************************************************************
* Author: Changhe Li & Ming Yang
* Email: changhe.lw@google.com Or yangming0702@gmail.com
* Language: C++
*************************************************************************
*  This file is part of EAlib. This library is free software;
*  you can redistribute it and/or modify it under the terms of the
*  GNU General Public License as published by the Free Software
*  Foundation; either version 2, or (at your option) any later version.
*************************************************************************/
// Created: 12 Nov 2011
// Last modified:

#include "GAIndividual.h"
#include "../../Problems/DOPs/BinaryDOP.h"

int GAIndividual::ms_numMutation=0;
int GAIndividual::ms_numXover=0;

GAIndividual::GAIndividual():Individual(),m_distribution(100),m_xoverProb(0.8),m_mutationStratey(MUTAT_NORMAL),m_mutationStep(0.1),m_flag(false)
{
    //ctor

    switch( Global::gp_problem->getEncoding()){
    case C_DECIMAL:
        m_mutationProb=1./Global::g_dimNumber;
        break;
    case C_BINARY:
       if(Global::gp_problem->getId()==Binary_DOP){
        m_mutationProb=1./dynamic_cast<BinaryDOP*>(Global::gp_problem)->getLength();
       }
        break;
    default:
        Throw(Runtime_error("mutation not defined!"));
        break;
    }

}
GAIndividual::~GAIndividual()
{
    //dtor

}
GAIndividual::GAIndividual(const GAIndividual & other):Individual(const_cast<GAIndividual & > (other)){


     m_fitness=other.m_fitness;
     m_distribution=other.m_distribution;
     m_mutationProb=other.m_mutationProb;
     m_xoverProb=other.m_xoverProb;
     m_flag=other.m_flag;

}
 GAIndividual& GAIndividual::operator=(GAIndividual & rhs){
     if(this==&rhs) return *this;
     Individual::operator=(rhs);

     m_fitness=rhs.m_fitness;
     m_distribution=rhs.m_distribution;
     m_mutationProb=rhs.m_mutationProb;
     m_xoverProb=rhs.m_xoverProb;
     m_flag=rhs.m_flag;

     return *this;

 }
 void GAIndividual::setMutationStrategy(GAMutationStrategy s){
	 m_mutationStratey=s;

 }
 void GAIndividual::setMutationStep(double step){
	 m_mutationStep=step;

 }

void GAIndividual::mutate(){
    //operation performed must be after crossover

    switch( Global::gp_problem->getEncoding()){
        case C_DECIMAL:
			switch(m_mutationStratey){
				case MUTAT_POLYNOMIAL:
				polynomialMutate();
				break;
				case MUTAT_NORMAL:
				normalMutate();
				break;
			}
			break;
        case C_BINARY:
            binaryMutate();
            break;
        default:
            Throw(Runtime_error("mutation not defined!"));
            break;
    }

}

void GAIndividual::normalMutate(){
	bool flag=false;
	for (int site = 0; site < Global::g_dimNumber; site++){
		if(Global::gp_uniformAlg->Next()<=m_mutationProb){
			flag=true;
			ms_numMutation++;
			m_pself.getGene<double>(site)=m_pself.getGene<double>(site)+m_mutationStep*Global::gp_normalAlg->Next();
		}
	}
	if(flag) m_pself.validate();
}
void GAIndividual::polynomialMutate(){
/*===================================================================
Mutation Using polynomial probability distribution. Picks up a random
site and generates a random number u between -1 to 1, ( or between
minu to maxu in case of rigid boudaries) and calls the routine
get_delta() to calculate the actual shift of the value.

This is from http://www.iitk.ac.in/kangal/codes.shtml
:Single-objective GA code in C (for Windows and Linux).
    ====================================================================*/
double distance1,x,delta_l,delta_u,delta,u,upper,lower;
bool flag=false;
     for (int site = 0; site < Global::g_dimNumber; site++){
         if(Global::gp_uniformAlg->Next()<=m_mutationProb){
             ms_numMutation++;
             flag=true;
             if(Global::gp_problem->getBoundaryFlag<double>(site)){
                 Global::gp_problem->getSearchRange<double>(lower,upper,site);
                 x = m_pself.getGene<double>(site);
                 distance1 = lower - x;
                 delta_l = distance1/(upper - lower);
                 if (delta_l < -1.0)  delta_l = -1.0;

                 distance1 = upper - x;
                 delta_u = distance1/(upper - lower);
                 if (delta_u > 1.0)   delta_u = 1.0;

                 if (-1.0*delta_l < delta_u) delta_u = -1.0 * delta_l;
                 else delta_l = -1.0 * delta_u;
            }else{
                 delta_l = -1.0;
                 delta_u =  1.0;
            }
             u = Global::gp_uniformAlg->Next();
             /* calculation of actual delta value */
             delta = getDelta(u, delta_l, delta_u) * (upper - lower);
             m_pself.getGene<double>(site) += delta;
           }
    }
    if(flag) m_pself.validate();
}

void GAIndividual::xover(GAIndividual & mate){
/*====================================================================
CROSS - OVER  USING strategy of uniform 50% variables
  For one variable problem, it is crossed over as usual.
  For multivariables, each variable is crossed over with a probability
  of 50 % , each time generating a new random beta.
====================================================================*/
    switch( Global::gp_problem->getEncoding()){
        case C_DECIMAL:
            realXover(mate);
            break;
        case C_BINARY:
            binaryXover(mate);
            break;
        default:
            Throw(Runtime_error("crossover not defined!"));
            break;
    }
}

double GAIndividual::getDelta(double u, double delta_l, double delta_u)
/*==================================================================
For given u value such that   -1 <= u <= 1, this routine returns a
value of delta from -1 to 1. Exact value of delta depends on specified
n_distribution.

This is from http://www.iitk.ac.in/kangal/codes.shtml
:Single-objective GA code in C (for Windows and Linux).
====================================================================*/
{
  double delta, aa;

  if (u >= 1.0-1.0e-9)      delta = delta_u;
  else if (u <= 0.0+1.0e-9) delta = delta_l;
  else
    {
      if (u <= 0.5)
	{
	  aa = 2.0*u + (1.0-2.0*u)*pow((1+delta_l),(m_distribution + 1.0));
	  delta = pow(aa, (1.0 / (m_distribution + 1.0))) - 1.0;
	}
      else
	{
	  aa = 2.0*(1-u) + 2.0*(u-0.5)*pow((1-delta_u),(m_distribution + 1.0));
	  delta = 1.0 - pow(aa, (1.0 / (m_distribution + 1.0)));
	}
    }
  if(delta < -1.0 || delta > 1.0){
      Throw(Runtime_error("Error in mutation!!"));
    }
  return (delta);
}
 void GAIndividual::setDistribution(double dis){
    m_distribution=dis;
 }
void GAIndividual::setMutationProbability(double p){
    m_mutationProb=p;

}
void GAIndividual::setXoverProbability(double p){
    m_xoverProb=p;
}
 void GAIndividual::binaryMutate(){
    for (int i = 0; i < Global::g_dimNumber; i++){
        for(unsigned int j=0;j<m_pself.getGene(i).m_length;j++){
            if(Global::gp_uniformAlg->Next()<=m_mutationProb){
                ms_numMutation++;
                m_pself.getGene<bool>(i,j)=!m_pself.getGene<bool>(i,j);
            }
        }
    }
 }

void GAIndividual::binaryXover(GAIndividual & mate){
    if(Global::gp_uniformAlg->Next()>m_xoverProb) return;
    // single point crossover
    for (int i = 0; i < Global::g_dimNumber; i++){
        if(Global::g_dimNumber==1||Global::gp_uniformAlg->Next()<=0.5){
            int p=gRandInt(0,m_pself.getGene(i).m_length);
            for(unsigned int j=p;j<m_pself.getGene(i).m_length;j++){
                 ms_numXover++;
                 bool x;
                 x=m_pself.getGene<bool>(i,j);
                m_pself.getGene<bool>(i,j)=mate.m_pself.getGene<bool>(i,j);
                mate.m_pself.getGene<bool>(i,j)=x;
            }
        }
    }
}
void GAIndividual::realXover(GAIndividual & mate){
    //Arithmetical crossover
    if(Global::gp_uniformAlg->Next()>m_xoverProb) return;
    for(int i=0;i<Global::g_dimNumber;i++){
        if(Global::g_dimNumber==1||Global::gp_uniformAlg->Next()<=0.5){
             ms_numXover++;
			double r=Global::gp_uniformAlg->Next(); //normally r=0.25
			m_pself.getGene<double>(i)= r*m_pself.getGene<double>(i)+(1-r)*mate.m_pself.getGene<double>(i);
			mate.m_pself.getGene<double>(i)=r*mate.m_pself.getGene<double>(i)+(1-r)*m_pself.getGene<double>(i);
        }
    }
}
void GAIndividual::increaseDimension(){
    Individual::increaseDimension();
    m_backup.increaseDimension();
}
void GAIndividual::decreaseDimension(){
    Individual::decreaseDimension();
    m_backup.decreaseDimension();

}
