/*

Generate a Fitness Landscape on which each genotype (that is encoded by a long int)
has an associated fitness value

*/

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <string.h>
#include <math.h>
#include "random.h"
#include "landscape.h"
#include "genotypes.h"
#include "summary_statistics.h"

void print_genotype( int *genotype, int nlocus ){

int i;

for(i=0;i<nlocus-1; i++)
printf("%d ", genotype[i] );

printf("%d", genotype[nlocus-1] );

}
void print_intgenotype(int g, struct landscape *h){

int *genotype=int2genotype( *h, g , NULL);
print_genotype( genotype , h->nlocus );
free( genotype );

}

int *int2genotype( struct landscape h, int x , int *genotype)
{
int l=h.nlocus-1;

if(genotype == NULL){
genotype = (int *)malloc( (size_t) h.nlocus*sizeof(int) );
if( ! genotype )fprintf(stderr, "int2genotype: cannot allocate the genotype, bye\n"), exit(3);
}

while( l >= 0 ){
genotype[l] = x%h.alleles[l];

x = x/h.alleles[l];
l--;
}

return genotype;
}

/*
built the index number that corresponds to the genotype
*/
int genotype2int( struct landscape h, int *genotype)
{

int l=h.nlocus-1;
int m=1;
int x=0;

while( l >= 0 ){

x += genotype[l]*m;
m*=h.alleles[l];
l--;
}

return (x);
}

int isvalueinarray(int val, int *arr, int size)
{
int i;
for (i=0; i < size; i++)
{
if (arr[i] == val)
{
return (1);
}
}
return (0);
}

void init_landscape( struct landscape *l, int nlocus, int *alleles)
{

int i;

l->nlocus = nlocus;
l->neighbors = 0;
l->nalleles = 0;
l->alleles = (int *)malloc( (size_t) nlocus*sizeof(int) );
l->nalleles = 0;

if(! l->alleles )fprintf(stderr, "init_landscape: cannot allocate alleles, bye\n"), exit(3);

for(i=0;i<nlocus; i++)
{
l->alleles[i] = alleles[i];
l->nalleles += alleles[i];
}

l->ngenotypes=1;
for(i=0;i<l->nlocus;i++)
l->ngenotypes *= l->alleles[i];

l->fitness = (float *) malloc( (size_t) l->ngenotypes*sizeof(float) );
if( !l )fprintf(stderr, "cannot allocate fitness\n" ), exit(3);

for (i=0;i<l->ngenotypes;i++)
l->fitness[i] = DEFAULT_FITNESS; /* it should be no fitness but be carefull... */

for(i=0;i<l->nlocus;i++)
l->neighbors += (l->alleles[i]-1);

l->log_scale = 0;
}

void print_landscape( struct landscape *l )
{

int g;
int *geno=NULL;

printf("landscape (%d loci; %d genotypes): ", l->nlocus, l->ngenotypes);
print_genotype( l->alleles, l->nlocus );
printf("\n");

for(g=0; g<l->ngenotypes; g++ ){

geno = int2genotype( *l, g , geno );
printf("genotype: %d '", g);
print_genotype( geno, l->nlocus );
printf("'  %f\n",  l->fitness[g] );

}
printf("neighbors:%d\n",l->neighbors);
printf("max=%f min=%f\n",l->minf,l->maxf);

free(geno);
}

void output_landscape( struct landscape *l )
{

int g;
int *geno=NULL;

print_genotype( l->alleles, l->nlocus );
printf("\n");

for(g=0; g<l->ngenotypes; g++ ){

geno = int2genotype( *l, g , geno );
print_genotype( geno, l->nlocus );
printf(" %f\n",  l->fitness[g] );

}

free(geno);
}

void free_landscape( struct landscape *l )
{
free(l->alleles);
free(l->fitness);
}

void log_landscape( struct landscape *fl )
{

int g;

/*
Basic Check
*/
for(g=0;g< fl->ngenotypes; g++ )
{
if(fl->fitness[g] <= 0 && fl->fitness[g]!=DEFAULT_FITNESS)
{
printf("<BR><BR>log_landscape: the FL contains <=0 (fitness=%f) fitness values, cannot use log(fitness)\n<BR>",fl->fitness[g]);
print_intgenotype(g, fl);
exit (1);
}
}

/*
Change into logscale
*/
for(g=0;g< fl->ngenotypes; g++ )
{
if (fl->fitness[g]!=DEFAULT_FITNESS)
{
fl->fitness[g] = log( fl->fitness[g] );
}
}

fl->log_scale++;

return;
}

void exp_landscape( struct landscape *fl )
{

int g;

/*
Change into exp scale
*/
for(g=0;g< fl->ngenotypes; g++ )
{
fl->fitness[g] = exp( fl->fitness[g] );
}

fl->log_scale--;

return;
}

/*
w is the weight of the first one
*/
struct landscape Merge( struct landscape *fl1, struct landscape *fl2, float w )
{

struct landscape res;
int i;

if (w<0 || w>1)
{
fprintf(stderr,"weight should be between 0 and 1\n"),exit(1);
}

if( fl1->ngenotypes != fl2->ngenotypes )
{
fprintf(stderr,"cannot merge FL of different sizes\n"),exit(1);
}

init_landscape( &res, fl1->nlocus, fl1->alleles);

/*
Combine using weights in logscale
*/
for (i=0;i<res.ngenotypes;i++)
{
res.fitness[i] =  w*fl1->fitness[i] + (1-w)*fl2->fitness[i];
}

return (res);
}

void remove_negative( struct landscape *fl, char opt_cut )
{

float min=fl->fitness[0];
int i;

if(opt_cut == 0)
{

for(i=1;i<fl->ngenotypes;i++)
{
if(fl->fitness[i]<min)
{
min=fl->fitness[i]<min;
}
}

if(min<0)
{
for(i=0;i<fl->ngenotypes;i++)
{
fl->fitness[i] -= min;
}
}

}
else
{
for (i=0;i<fl->ngenotypes;i++)
{
if( fl->fitness[i] <= 0)
{
fl->fitness[i] = 0;
}
}
}

}