#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#include <stdio.h>
#include <errno.h>

#ifdef HAVE_IEEEFP_H
#include <ieeefp.h> 
#endif

#ifdef HAVE_NAN_H
#include <nan.h> 
#endif

#include "yagi.h"

#define granularity 8 /* 2^granularity = possible different lengths */

#define LENGTH_MIN            0.35 /* min length of an element (in lambda) */
#define LENGTH_DR_MIN         0.40 /* min length of an element (in lambda) */
#define LENGTH_MIN_REFLECTOR  0.45 /* min length of reflector (in lambda) */
#define LENGTH_MAX            0.50 /* max length of an element (in lambda) */
#define SPACE_MIN             0.05 /* min spacing between elements(in lambda) */
#define SPACE_MAX             0.42 /* max spacing between elements(in lambda) */
#define MAX_CHANGE
extern int popsize;
extern int iterations;

double **data_driveng, **data_parasiticg, *vg, **zg, **Ag, *bg;
double *ping, design_frequencyg;
int driveng, parasiticg, *indxg, elementsg;
struct FCOMPLEX *voltageg, *currentg, *input_impedanceg;
struct element_data *coordinatesg;
double lambda;
struct performance_data *mean_performanceg;
struct flags flagg;
char *output_filenameg, *update_filenameg;
double min_frequencyg, max_frequencyg, step_frequencyg, angular_stepg;
int k;

void genetic_algorithm(char *output_filename, char *update_filename, struct flags flag, double design_frequency, double min_frequency, double max_frequency, double step_frequency, double angular_step, int driven, int parasitic, double **data_driven, double **data_parasitic, double *v, double **z, double *pin, struct FCOMPLEX *voltage, struct FCOMPLEX *current, struct FCOMPLEX *input_impedance, struct element_data *coordinates, double **A, double  *b, int *indx,struct performance_data *mean_performance) 
{
	int elements;
	elements=driven+parasitic;
	/* Set up global pointers; save modifiy GA code too much */
	data_driveng=data_driven;
	data_parasiticg=data_parasitic;
	ping=pin; 
	mean_performanceg=mean_performance;
	vg=v;
	zg=z;
	Ag=A;
	bg=b;
	voltageg=voltage;
	currentg=current;
	elementsg=elements;
	input_impedanceg=input_impedance;
	coordinatesg=coordinates;
	indxg=indx;
	driveng=driven;
	flagg=flag;
	parasiticg=parasitic;
	design_frequencyg=design_frequency;
	min_frequencyg=min_frequency;
	max_frequencyg=max_frequency;
	step_frequencyg=step_frequency;
	angular_stepg=angular_step;
	output_filenameg=output_filename;
	update_filenameg=update_filename;
	if(popsize ==0) /* ie  not set by user */
		popsize = 40;

	lambda = 3e8/design_frequency;  
	/* Code with an granularity (eg 8) bit string the length in the range 0 to 
	1, wavelength. Multiply by lambda to get true lengths.

	Likewise code spacing between individual elements as a 8 bit
	string, then convert to metres by multiplying by lambda */
	Initialise(popsize,2*granularity*elements-granularity);
	SetPrint(0) ;
	for(k=1; k<=iterations; ++k)
	{
		if(k%1==0)
		    SetPrint(0) ;
		else
			SetPrint(0);
		end_if_stop_exists(&k,iterations,5);
		Selection(stdout,k) ;
	}
	GA_Free();
}

double Objective(char *gene) 
{
		int i;  
		double fitness,lmax;
		static double max_fitness=0;
		static int run_first_time=1;
		if(run_first_time==1)
		{
			max_fitness=get_genetic_algorithm_fitness(flagg, design_frequencyg,driveng,parasiticg,data_driveng, data_parasiticg,vg, zg, ping, voltageg, currentg, input_impedanceg, coordinatesg, Ag, bg, indxg, mean_performanceg);
			do_since_better(0,output_filenameg, update_filenameg, *input_impedanceg,*mean_performanceg, flagg, "Optimised with the genetic algorithm", design_frequencyg, min_frequencyg,max_frequencyg,step_frequencyg, elementsg, driveng, parasiticg,angular_stepg,data_driveng,data_parasiticg,1.0, max_fitness); 
			run_first_time=0;
		} 
		data_parasiticg[1][X]=0.0; /* Set reflector at x=0 */
		/* Find the length of the reflector */
			data_parasiticg[1][LENGTH]=(ss2r(gene,0,granularity)*(LENGTH_MAX-LENGTH_MIN_REFLECTOR)+LENGTH_MIN_REFLECTOR)*lambda;
		for(i=1; i<=driveng; i++) /* for every driven element */
		{
			data_driveng[i][X]=(ss2r(gene,granularity,granularity)*(SPACE_MAX-SPACE_MIN)+SPACE_MIN)*lambda;
			data_driveng[i][LENGTH]=(ss2r(gene,2*granularity,granularity)*(LENGTH_MAX-LENGTH_DR_MIN)+LENGTH_DR_MIN)*lambda; 

		}
		/* put directors in + x direction */
		lmax=LENGTH_MAX; /* must be longer than driven */
		/* printf("  dr=%f lambda = %f m\n",data_driveng[1][LENGTH],data_driveng[1][LENGTH]*lambda);  */
		for(i=2; i<=parasiticg; i++)
		{
			if(i==2) /* put first director ahead of the driven element */
			data_parasiticg[i][X]=data_driveng[1][X] +(ss2r(gene,granularity*2*i-granularity,granularity)*(SPACE_MAX-SPACE_MIN)+SPACE_MIN)*lambda;  
			else /* These directors must be ahead of the last director */
		 data_parasiticg[i][X]=data_parasiticg[i-1][X] +(ss2r(gene,granularity*2*i-granularity,granularity)*(SPACE_MAX-SPACE_MIN)+SPACE_MIN)*lambda;  
			data_parasiticg[i][LENGTH]=(ss2r(gene,granularity*2*i-granularity,granularity)*(lmax-LENGTH_MIN)+LENGTH_MIN)*lambda; 
			/* printf("l=%f\n",data_parasiticg[i][LENGTH]/lambda);  */
			lmax=data_parasiticg[i][LENGTH]/lambda;
			if(lmax>LENGTH_MAX)
				lmax=LENGTH_MAX;
		}
		/* printf("\n");     */
		fitness=get_genetic_algorithm_fitness(flagg, design_frequencyg,driveng,parasiticg,data_driveng, data_parasiticg,vg, zg, ping, voltageg, currentg, input_impedanceg, coordinatesg, Ag, bg, indxg, mean_performanceg);
		if(fitness > max_fitness)
		{
			max_fitness=fitness;
			do_since_better(k,output_filenameg, update_filenameg, *input_impedanceg,*mean_performanceg, flagg, "Optimised with the genetic algorithm", design_frequencyg, min_frequencyg,max_frequencyg,step_frequencyg, elementsg, driveng, parasiticg,angular_stepg,data_driveng,data_parasiticg,1.0, fitness); 
		}

#ifdef DEBUG
	if(errno)
	{
	fprintf(stderr,"Errno =%d in Objective() of genetic.c\n", errno);
	exit(1);
	}
#endif
		return(fitness);
}

double ss2r(char *string,int pos,int len)
{
		double result, x ;
		int loop;

		result=0 ;
		for (loop=0 ; loop<len ; loop++) /* XXXXXXXXXXXXXXX */
			result=result+result+string[pos+loop]-'0' ;     
		
		x=(result+1)/((double)(1<<granularity)); 
		return(x);
}
