/*
**  gpc_io
**
**   gp checkpoint  population load/save routines
*/

/*
**   Scott "Jerry" Lawrence
**   2000 January 24,25
**   j@absynth.com
*/

/*
** $Id: gpc_io.c,v 1.2 2000/02/08 22:32:52 sdlpci Exp sdlpci $
**
** $Log: gpc_io.c,v $
 * Revision 1.2  2000/02/08  22:32:52  sdlpci
 * added population loading.
 *
 * Revision 1.1  2000/02/08  01:13:48  sdlpci
 * Initial revision
 *
*/

#include <stdio.h>
#include <stdlib.h>   /* for malloc/free */
#include <string.h>   /* for memset      */
#include "map.h"
#include "gp_strct.h"
#include "gp.h"
#include "util.h"
#include "io_util.h"

/* use whatever file the makefile wants us to */
char gpc_fname[] = CHECKFILE;

/* version number for the checkpoint file */
#define GPC_FILE_VERSION (1)

/* the plan of attack:
**
**  on initalization:
**       look for the checkpoint file.
**       if none exists, random seed a population
*/


/*
** file format is:
------------------------
V       # file version number
N       # number of population members
F G S   # fitness, generation, state
dna|dna|dna # dna
F G S   # fitness, generation, state
dna|dna|dna # dna
...
------------------------
**
*/

/*******************************************************************************
** population save routines
*/

char gpc_dna_types[]  = "nmc";

// dump out a specific dna strand
void gpc_dump_dna(FILE * fp, GP_LINE * node)
{
    if (!node) return;

    fprintf(fp, "    %3d  %c %3d %3d %3d  %c %3d  %3d %3d\n",
	    node->id,
	    gpc_dna_types[node->type],
	    node->type,
	    node->cond_type,
	    node->cond_value,
	    move_dirs[node->direction][0],
	    node->direction,
	    (node->line_then)? node->line_then->id : -1,
	    (node->line_else)? node->line_else->id : -1
	    );
    gpc_dump_dna(fp, node->line_then);
    gpc_dump_dna(fp, node->line_else);
}


// dump out the population to a file.
void gpc_save_population(GP_INDIVIDUAL ** pop)
{
    FILE * of;
    int i;
    int nnodes;

    if (!pop) return;

    /*
    ** dump out the population in a machine readable, 
    ** almost human readable form
    */

    of = fopen(gpc_fname, "wb");
    if (!of)
    {
	printf("%s: unable to open checkpoint file for write\n.", gpc_fname);
	return;
    }

    fprintf(of, "%d\t# gpc file version\n", GPC_FILE_VERSION);
    fprintf(of, "%d\t# pop size\n\n", population_size);
    fprintf(of, "#    id  op  op  ct  cv mv  mv  thn  els\n\n\n");

    for (i=0 ; i<population_size ; i++)
    {
	if (!pop[i]) break;

	nnodes = gp_enumerate_line(pop[i]->dna, 0);

	fprintf(of, "%d\t# individual number\n", i);
	fprintf(of, "%d %d %d\t# fit gen state\n", 
                    pop[i]->fitness, pop[i]->generation, pop[i]->state);
	fprintf(of, "%d\t# number of nodes, dna:\n", nnodes);
	gpc_dump_dna(of, pop[i]->dna);
	fprintf(of, "\n\n");
    }

    fclose(of);
}


/*******************************************************************************
** population load routines
*/


// if we load these the same order we saved them, we should be fine.
GP_LINE * gpc_load_dnatree(FILE * fp)
{
    int id,op,ct,cv,mv,thn,els;
    char junk;
    GP_LINE * node = (GP_LINE *) malloc (sizeof (GP_LINE) );

    if (!node){
	printf("bad alloc\n");
	return NULL;
    }

    id = read_int(fp);
    junk = read_char(fp);
    op = read_int(fp);
    ct = read_int(fp);
    cv = read_int(fp);
    junk = read_char(fp);
    mv = read_int(fp);
    thn = read_int(fp);
    els = read_int(fp);

    node->id         = id;
    node->type       = op;
    node->cond_type  = ct;
    node->cond_value = cv;
    node->direction  = mv;
    node->line_then  = NULL;
    node->line_else  = NULL;

    if (thn > 0)  node->line_then = gpc_load_dnatree(fp);
    if (els > 0)  node->line_else = gpc_load_dnatree(fp);

    return node;
}


// load in the population
GP_INDIVIDUAL ** gpc_load_population(GP_INDIVIDUAL ** population, 
                                               int  * pop_size)
{
    FILE * fp;
    GP_INDIVIDUAL ** pop = NULL;
    int file_version;
    int file_popsize;
    int i_no;


    fp = fopen(gpc_fname, "rb");
    if (!fp)
    {
	printf("%s: unable to open checkpoint file for read\n.", gpc_fname);
	return population;
    }

    file_version = read_int(fp);

    if (file_version < 1 || file_version > 1)
    {
	printf("checkpoint file version %d is bad\n", file_version);
	fclose(fp);
	return population;
    }

    gp_genocide();  // we mean business here...

    file_popsize = read_int(fp);
    if (file_popsize<=0 || file_popsize>30000)
    {
	printf("illegal population size: %d\n", file_popsize);
	fclose(fp);
	return population;
    }

    pop = (GP_INDIVIDUAL **) malloc(sizeof (GP_INDIVIDUAL *) * file_popsize);
    
    // now load in the population...
    for (i_no=0 ; i_no<file_popsize ; i_no++)
    {
	int file_ind     = read_int(fp);
	int file_fitness = read_int(fp);
	int file_gen     = read_int(fp);
	int file_states  = read_int(fp);
	int file_nodes   = read_int(fp);

	// these two are for code readability
	file_nodes = file_nodes;  
	file_gen   = file_ind;

	pop[i_no] = (GP_INDIVIDUAL *) malloc(sizeof (GP_INDIVIDUAL) );;
	if (!pop[i_no])
	{
	    printf("alloc error.  bummer.\n");
	    free(pop);
	    fclose(fp);
	    return population;
	}

	pop[i_no]->fitness    = file_fitness;
	pop[i_no]->generation = file_gen;
	pop[i_no]->state      = file_states;

	pop[i_no]->dna = gpc_load_dnatree(fp);
    }

    fclose(fp);

    *pop_size = file_popsize;
    return pop;
}

