library(ggplot2)

func.g <- function(x) x
func.k <- function(x) sin(x)
func.h <- function(x) x * sin(x)
func.l <- function(x) 2 + cos(x) + sin(2*x)


delta.const <- function(x) {
    return(function() x)
}

delta.gaus <- function() {
    return(rnorm(1))
}

ea.init_population <- function(range, size, rand_gen) {
    return(rand_gen(size) * (range[2] - range[1]) + range[1])   
}    

ea.trace <- function(range, delta, population_size, fit_func, iterations) {
    population <- ea.init_population(range, population_size, runif)
    
    df <- data.frame(i=integer()
                     ,max=numeric()
                     ,median=numeric()
                     ,min=numeric())

    for(i in 1:iterations) {
        population <- ea.iterate(delta, population, fit_func)

        df[nrow(df) + 1,] <- c(i
                               ,population[1]
                               ,population[length(population) %/% 2]
                               ,population[length(population)]
                              )
    }

    df["max_val"] <- fit_func(df$max)
    df["median_val"] <- fit_func(df$median)
    df["min_val"] <- fit_func(df$min)

    res <- list(population, df)
    names(res) <- c("population", "df_tr")

    return(res)
}

ea.traces <- function(range, deltas, population_size, fit_funcs, iterations) {
    df <- data.frame(i=integer()
                     ,max=numeric()
                     ,median=numeric()
                     ,min=numeric()
                     ,delta_func=character()
                     ,delta=numeric()
                     ,fit_func=character())

    for(delta_func in names(deltas)) {
        delta <- deltas[delta_func]

        for(fit_name in names(fit_funcs)) {
            fit <- fit_funcs[fit_name]

            tmp_df <- ea.trace(range, delta, population_size, fit, iterations)$df_tr
 
            tmp_df["delta_func"] <- delta_func
            if(delta_func == "delta.gaus") {
                tmp_df["delta"] <- NA
            } else {
                tmp_df["delta"] <- delta()
            }
    
            tmp_df["fit_func"] <- fit_name

            df <- rbind(df, tmp_df)
        }
    }

    return(df)
}

ea.plot <- function(range, delta, population_size, fit_func, iterations) {
    res <- ea.trace(range, delta, population_size, fit_func, iterations)

    df_vals <- melt(res$df_tr[c("i", "min_val", "median_val", "max_val")], id.vars="i")

    p <- ggplot(data=df_vals, aes(x=i)) +
            geom_line(aes(y=value, linetype=variable))

    return(p)
}

ea.run <- function(range, delta, population_size, fit_func, iterations) {
    population <- ea.init_population(range, population_size, runif)

    for(i in 1:iterations) {
        population <- ea.iterate(delta, population, fit_func)
    }

    return(population)
}

ea.iterate <- function(delta, population, fit_func) {
    children <- c()

    for(individual in population) {
        children <- append(children, ea.mutate(individual, delta))
    }

    population <- append(population, children)

    return(ea.select(population, fit_func))
}

ea.mutate <- function(individual, delta) {
    sign <- sample(c(-1,1), 1)
    
    return(individual + sign * delta())
}

ea.select <- function(population, fit_func) {
    sorted_popul <- population[order(sapply(population, fit_func), decreasing=TRUE)]
    return(sorted_popul[1 : (length(sorted_popul) %/% 2)])
}
