import numpy as np
from numpy import abs, cos, exp, mean, pi, prod, sin, sqrt, sum ,random,meshgrid
import math 
from config import Config as cf
import function as fn
from scipy.stats import levy
import random

def levy_flight(Lambda):
    #generate step from levy distribution
    sigma1 = np.power((math.gamma(1 + Lambda) * np.sin((np.pi * Lambda) / 2)) \
                      / math.gamma((1 + Lambda) / 2) * np.power(2, (Lambda - 1) / 2), 1 / Lambda)
    sigma2 = 1
    u = np.random.normal(0, sigma1, size=cf.get_dimension())
    v = np.random.normal(0, sigma2, size=cf.get_dimension())
    step = u / np.power(np.fabs(v), 1 / Lambda)

    return step    # return np.array (ex. [ 1.37861233 -1.49481199  1.38124823])


def ackley( x, Ub, Lb, a=20, b=0.2, c=2*pi ):
    x = np.asarray_chkfinite(x)  # ValueError if any NaN or Inf
    n = len(x)
    s1 = sum( x**2 )
    s2 = sum( cos( c * x ))
    return -a*exp( -b*sqrt( s1 / n )) - exp( s2 / n ) + a + exp(1)

#...............................................................................
michalewicz_m = 10  # orig 10: ^20 => underflow

def michalewicz( x, Ub, Lb):  # mich.m
    x = np.asarray_chkfinite(x)
    n = len(x)
    j = np.arange( 1., n+1 )
    list = [- sum( sin(x) * sin( j * x**2 / pi ) ** (2 * michalewicz_m) ), - sum( sin(x) * sin( j * x**2 / pi ) ** (5 * michalewicz_m) ), - sum( sin(x) * sin( j * x**2 / pi ) ** (10 * michalewicz_m) )]
    return list
#............................................................................... 

def Scaffer( x1 , x2):
    x = x1**2 + x2**2
    xneg = x1**2 - x2**2
    y = sin (xneg) * sin (xneg)
    return 0.5+( y-0.5) / (1+ (0.001* (x)**2) )

def Scaffer2( x1 , x2):
    x = x1**2 + x2**2
    xneg = sqrt(x1**2 + x2**2)
    y = sin (xneg) * sin (xneg)
    return 0.5+( y-0.5) / (1+ (0.001* (x)**2) )

def griewank( x, Ub, Lb,fr=4000 ):
    x = np.asarray_chkfinite(x)
    n = len(x)
    j = np.arange( 1., n+1 )
    s = sum( x**2 )
    p = prod( cos( x / sqrt(j) ))
    return s/fr - p + 1

def levy( x ):
    x = np.asarray_chkfinite(x)
    n = len(x)
    z = 1 + (x - 1) / 4
    return (sin( pi * z[0] )**2
        + sum( (z[:-1] - 1)**2 * (1 + 10 * sin( pi * z[:-1] + 1 )**2 ))
        +       (z[-1] - 1)**2 * (1 + sin( 2 * pi * z[-1] )**2 ))
#...............................................................................
def rastrigin( x, Ub, Lb):  # rast.m
    x = np.asarray_chkfinite(x)
    n = len(x)
    return 10*n + sum( x**2 - 10 * cos( 2 * pi * x ))

#...............................................................................
def rosenbrock( x, Ub, Lb ):  # rosen.m
    x = np.asarray_chkfinite(x)
    x0 = x[:-1]
    x1 = x[1:]
    return (sum( (1 - x0) **2 )
        + 100 * sum( (x1 - x0**2) **2 ))

def schwefel_2_2( x, Ub, Lb ):  # schw.m
    x = np.asarray_chkfinite(x)
    n = len(x)
    
    return sum(abs(x))+ pi* (sum(abs(x)))

def zetl(x1,x2):
    return 0.25* x1 + (x1**2 - 2*x1 + x2**2)**2

#...............................................................................
def schwefel_2_26( x, Ub, Lb ):  # schw.m
    x = np.asarray_chkfinite(x)
    n = len(x)
    
    return 418.9829*n - sum( x * sin( sqrt( abs( x ))))

#...............................................................................
def zakharov( x, Ub, Lb) :  # zakh.m
    x = np.asarray_chkfinite(x)
    n = len(x)
    j = np.arange( 1., n+1 )
    s2 = sum( j * x ) / 2
    return sum( x**2 ) + s2**2 + s2**4

#...............................................................................
def sphere(array):
    fitness = 0
    for i in range(len(array)):
        fitness = fitness + array[i]**2
    return fitness

def bentCigar( x ):
     
    x = np.asarray_chkfinite(x)
    i=x[0]
    x=  np. delete(x, 0)
    return i + 10**6 *sum(x**2)

def Styblinski(x):
    x = np.asarray_chkfinite(x)
    return sum(x**4 - 16*x**2 +5*x) / 4 

def discuss( x ):
     
    x = np.asarray_chkfinite(x)
    i=x[0]
    x=  np. delete(x, 0)
    return 10**6*i**2 + 10**6 *sum(x**2)

def RHE(x1,x2):
    return (x1*x1+x2*x2);


def Leon(x1,x2):
    return 100*(x2-x1**2)+(1-x1)**2

def hybrid2(x):
    return sphere(x)+ackley(x, -100, 100)+ Scaffer2(x[0], x[1])

def hybrid(x):
    return rastrigin(x, -100, 100)+ bentCigar(x) + griewank(x, -100, 100)


#...............................................................................
class Individual:
    def __init__(self):
        self.__position = np.random.rand(cf.get_dimension()) * (cf.get_max_domain() - cf.get_min_domain())  + cf.get_min_domain()
        self.__fitness = fn.calculation(self.__position,0) # iteration = 0

    def get_position(self):
        return self.__position

    def set_position(self, position):
        self.__position = position

    def get_fitness(self):
        return self.__fitness

    def set_fitness(self, fitness):
        self.__fitness = fitness

    def abandon(self):
        # abandon some variables
        for i in range(len(self.__position)):
            p = np.random.rand()
            if p < cf.get_Pa():
                self.__position[i] = np.random.rand() * (cf.get_max_domain() - cf.get_min_domain())  + cf.get_min_domain()

    def get_cuckoo(self):

        step_size = cf.get_stepsize() * levy_flight(cf.get_lambda())

        # Update position
        self.__position = self.__position + step_size

        # Simple Boundary Rule
        for i in range(len(self.__position)):
            if self.__position[i] > cf.get_max_domain():
                self.__position[i] = cf.get_max_domain()
            if self.__position[i] < cf.get_min_domain():
                self.__position[i] = cf.get_min_domain()

    def print_info(self,i):
        print("id:","{0:3d}".format(i),
              "|| fitness:",str(self.__fitness).rjust(14," "),
              "|| position:",np.round(self.__position,decimals=4))


if __name__ == '__main__':
  
    #randomlist = random.sample(range(10, 30), 5)
    print(levy([0,0]))
    print(griewank([-1,1], -1, 1))
    print(ackley([0,0], 0, 0))
    print(michalewicz([2.20,1.57], 0,pi))
    #print(schwefel_1_6([-0,0],100,100))
    print(int(schwefel_2_26([420.9687,420.9687],420.9687,420.9687)))
    print(rosenbrock([1,1],-30, 30))
    print(zakharov([0,0],-5,10))
    print(sphere([0,0]))
    print(rastrigin([0,0], 0, 0))
    print(bentCigar([0,0]))
    print(discuss([0,0]))
    print(RHE(0,0))
    print(schwefel_2_2([0,0],0,0))
    print(Leon(1, 1))
    print(Scaffer(0, 00))
    print(Scaffer2(0, 0))
    print(zetl(-0.0299, 0))
    print(hybrid([0.33,0.33,0.33]))
    print(hybrid2([0.33,0.33,0.33]))
    print(Styblinski([-2.903534,-2.903534]))
    #f_Sphere (R_1 Z_1 )+f_Ackley (R_2 Z_1 )+f_SchafferF7 (R_n Z_1 )