/*
   RealBrain
   realbrain.c

  	 This is an implementation of the Adaptrode-based neural network
   software for the MAVRIC robot.  This implementation assumes a 
   graded signal as opposed to the action potential signal used in the
   standard Adaptrode model.

     The adaptrode, artificial neuron based on it and the neural network
   processor implemented in this software is protected under 
   U.S. Patent #5,504,839. Permission to use this software freely for
   research purposes is hereby given by the holder of that patent and
   the author of this software.  This software may not be used for any
   commercial purpose or without giving the holder appropriate   
   acknowledgement.

  Author: George Mobus
  Date: 3/31/00
  Version: 1.0
  Revision History:
     Revised: 1/17/01 - Added extinction support to processBrain()
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "realbrain.h"

void initBrain(Brain * b)
{
	if (!b) return;
	b->atype_cnt = 0;
	b->ntype_cnt = 0;
	b->inslot_cnt = 0;
	b->neuron_cnt = 0;
	b->outslot_cnt = 0;
	b->atypes = NULL;
	b->inslots = NULL;
	b->neurons = NULL;
	b->outslots = NULL;
}

void initAtype(Atype * at)
{
	int i;

	if (!at) return;
	at->id = NONE;
	at->decay = 0.25;
	at->extinction = 0.4;
	at->wt_max = 0.5;
	at->wt_min = 0;
	at->alpha[0] = 0.4;
	at->delta[0]= 0.1;
	for (i=1; i<4; i++) {
		at->alpha[i] = at->alpha[i-1] * 0.1;
		at->delta[i] = at->delta[i-1] * 0.01;
	}
}

void initNtype(Ntype * nt)
{
	if (!nt) return;
	nt->id = NONE;
	nt->US_src = NEURON;
	nt->t_decay = 0.25;
	nt->t_alpha = 0.4;
}

void initInslot(Inslot * in)
{
	if (!in) return;
	in->id = NONE;
	in->greyscale = 0;
	in->value = 0.0;
}

void initOutslot(Outslot * o)
{
	if (!o) return;
	o->id = NONE;
	o->greyscale = 0;
	o->value = 0.0;
	o->neuron_id = -1;
}

void initNeuron(Neuron * n)
{
	if (!n) return;
	n->id = NONE;
	n->ntype_id = NONE;
	n->ntype = NULL;
	n->out0 = 0.0;
	n->out1 = 0.0;
	n->thresh = 0.5;
	n->t_base = 0.5;
	n->sum = 0.0;
	n->phase = 0;
	n->age = 0L;
	n->adapt_cnt = 0;
	n->reward_src = NONE;
	n->reward_src_type = NONE;
	n->punish_src = NONE;
	n->punish_src_type = NONE;
	n->confirm_src = NONE;
	n->confirm_src_type = NONE;
	n->t_up_src = NONE;
	n->t_up_src_type = NONE;
	n->t_dwn_src = NONE;
	n->t_dwn_src_type = NONE;
	n->t_up = NULL;
	n->t_dwn = NULL;
	n->synapses = NULL;
	n->reward = n->punish = n->confirm = NULL;
}

void initAdaptrode(Adaptrode * a)
{
	int i;

	if (!a) return;
	a->id = NONE;
	a->atype_id = 0;
	a->atype = NULL;
	a->learn = 0;
	a->extinguish = 0;
	a->type = FIXED;
	a->src_type = NONE;
	a->src_id = 0;
	a->src = NULL;
	a->response = 0.0;
	a->w_max = 0.5;
	for (i=0; i<4; i++) {
		a->w[i] = 0.0;
	}
	a->age = 0L;
}

void linkBrain(Brain *b)
{
/*
	This function establishes pointer links between adaptrodes and the
	sources of various signals.  References to specific sources are made
	which require objects to exist.
	PRECONDITION: all relevant arrays have been created, initialized or 
	              read from either a config or hib file.
	POSTCONDITION: all pointer values have been filled (made not NULL) to
	               existing strucutures.
*/
	int i, j;
	Neuron *n = NULL;		/* convenience pointers */
	Adaptrode *a = NULL;

	if (!b) return;
	for (i=0; i<b->neuron_cnt; i++) {	/* for every neuron in brain do */
		n = &(b->neurons[i]);			/* get convenient pointer */
		n->ntype = &(b->ntypes[n->ntype_id]); /* set ntype pointer */
		switch (n->t_up_src_type) {		/* depending on which threshold 
										   dynamics are to be used */
			case NONE: { /*do nothing */ break;}
			case SELF: {
				n->t_up = &n->out0;		/* neuron is its own modulator */
				break;
			}
			case SLOT: {
				n->t_up = &(b->inslots[n->t_up_src].value);
				break;
			}
			case NEURON: {
				n->t_up = &(b->neurons[n->t_up_src].out0);
										/* another neuron is the modulator */
				break;
			}
			case ADAPTRODE: {
				n->t_up = &(n->synapses[n->t_up_src].w[0]);
										/* one of the local adaptrodes */
				break;
			}
		}
		switch (n->t_dwn_src_type) {
			case NONE: { /*do nothing */ break;}
			case SELF: {
				n->t_dwn = &n->out0;
				break;
			}
			case SLOT: {
				n->t_dwn = &(b->inslots[n->t_dwn_src].value);
				break;
			}
			case NEURON: {
				n->t_dwn = &(b->neurons[n->t_dwn_src].out0);
				break;
			}
			case ADAPTRODE: {
				n->t_dwn = &(n->synapses[n->t_dwn_src].w[0]);
				break;
			}
		}
		switch (n->reward_src_type) {		/* set reward pointer */
			case NONE: { /*do nothing */ break;}
			case SLOT: {
				n->reward = &(b->inslots[n->reward_src].value);
				break;
			}
			case NEURON: {
				n->reward = &(b->neurons[n->reward_src].out0);
				break;
			}
		}
		switch (n->punish_src_type) {
			case NONE: { /*do nothing */ break;}
			case SLOT: {
				n->punish = &(b->inslots[n->punish_src].value);
				break;
			}
			case NEURON: {
				n->punish = &(b->neurons[n->punish_src].out0);
				break;
			}
		}
		switch (n->confirm_src_type) {
			case NONE: { /*do nothing */ break;}
			case SLOT: {
				n->confirm = &(b->inslots[n->confirm_src].value);
				break;
			}
			case NEURON: {
				n->confirm = &(b->neurons[n->confirm_src].out0);
				break;
			}
		}
		/* link each adaptrode in each category */
		for (j=0; j<n->adapt_cnt; j++) {
			a = &(n->synapses[j]);
			a->atype = &(b->atypes[a->atype_id]);

			if (a->src_type == SLOT) {
				a->src = &(b->inslots[a->src_id].value);
			}
			else a->src = &(b->neurons[a->src_id].out0);
		}
	} /*end for each neuron loop */
	for (i=0; i<b->outslot_cnt; i++) 
		b->outslots[i].src = &(b->neurons[b->outslots[i].neuron_id].out0);
	/* End linkBrain() */
}

void readBrain(Brain * b, FILE *infile)
{
	char	buff[256], *p;
	int i,j,k;
	Neuron *n;
	Adaptrode *a;

	if (!b || !infile) return;
	fgets(buff,255,infile);
	while ((buff[0] == '#') || (buff[0] == '\n')) {
		fgets(buff,255,infile); 
	} 
	p = strtok(buff,", \n");
	if (!p) fgets(buff,255,infile);
	b->atype_cnt = atoi(p);
	b->ntype_cnt = atoi(strtok(NULL," \n"));
	b->inslot_cnt = atoi(strtok(NULL,", \n"));
	b->neuron_cnt = atoi(strtok(NULL,", \n"));
	b->outslot_cnt = atoi(strtok(NULL,", \n"));
	fgets(buff, 255, infile);
	while ((buff[0] == '#') || (buff[0] == '\n')) {
		fgets(buff,255,infile); 
	}
	/* Now get Atype info */
	b->atypes = (Atype *) malloc(sizeof(Atype) * b->atype_cnt);
	for (i=0; i<b->atype_cnt; i++) {
		initAtype(&(b->atypes[i]));
		p = strtok(buff,", \n");
		b->atypes[i].id = atoi(p);
		b->atypes[i].decay = atof(strtok(NULL,", \n"));
		b->atypes[i].extinction = atof(strtok(NULL,", \n"));
		b->atypes[i].wt_max = atof(strtok(NULL,", \n"));
		b->atypes[i].wt_min = atof(strtok(NULL,", \n"));
		for (j = 0; j<4; j++) {
			b->atypes[i].alpha[j] = atof(strtok(NULL,", \n"));
			b->atypes[i].delta[j] = atof(strtok(NULL,", \n"));
		}
		fgets(buff,255,infile);
	}
	while ((buff[0] == '#') || (buff[0] == '\n')) {
		fgets(buff,255,infile); 
	}
	/* Now get Ntype info */
	b->ntypes = (Ntype *) malloc(sizeof(Ntype) * b->ntype_cnt);
	for (i=0; i<b->ntype_cnt; i++) {
		initNtype(&(b->ntypes[i]));
		p = strtok(buff,", \n");
		b->ntypes[i].id = atoi(p);
		b->ntypes[i].US_src = atoi(strtok(NULL,", \n"));
		b->ntypes[i].t_decay = atof(strtok(NULL,", \n"));
		b->ntypes[i].t_alpha = atof(strtok(NULL,", \n"));
		fgets(buff,255,infile);
	}
	while ((buff[0] == '#') || (buff[0] == '\n')) {
		fgets(buff,255,infile); 
	}
	/* Get Inslots info */
	b->inslots = (Inslot *) malloc(sizeof(Inslot) * b->inslot_cnt);
	for (i=0; i<b->inslot_cnt; i++) {
		initInslot(&(b->inslots[i]));
		p = strtok(buff, ", \n");
		b->inslots[i].id = atoi(p);
		b->inslots[i].greyscale = atoi(strtok(NULL,", \n"));
		b->inslots[i].value = atof(strtok(NULL,", \n"));
		fgets(buff,255,infile);
	}
	while ((buff[0] == '#') || (buff[0] == '\n')) {
		fgets(buff,255,infile); 
	}
	/* Get Neurons info */
	b->neurons = (Neuron *) malloc(sizeof(Neuron) * b->neuron_cnt);
	for (i=0; i<b->neuron_cnt; i++) {
		initNeuron(&(b->neurons[i]));
		n = &(b->neurons[i]);

		p = strtok(buff,", \n");
		n->id = atoi(p);
		n->ntype_id = atoi(strtok(NULL,", \n"));
		n->out0 = atof(strtok(NULL,", \n"));
		n->out1 = atof(strtok(NULL,", \n"));
		n->thresh = atof(strtok(NULL,", \n"));
		n->t_base = atof(strtok(NULL,", \n"));
		n->sum = atof(strtok(NULL,", \n"));
		n->phase = atoi(strtok(NULL,", \n"));
		n->age = atol(strtok(NULL,", \n"));
		n->adapt_cnt = atoi(strtok(NULL,", \n"));
		/* get all linking information */
		n->reward_src = atoi(strtok(NULL,", \n"));
		n->reward_src_type = atoi(strtok(NULL,", \n"));
		n->punish_src = atoi(strtok(NULL,", \n"));
		n->punish_src_type = atoi(strtok(NULL,", \n"));
		n->confirm_src = atoi(strtok(NULL,", \n"));
		n->confirm_src_type = atoi(strtok(NULL,", \n"));
		n->t_up_src = atoi(strtok(NULL,", \n"));
		n->t_up_src_type = atoi(strtok(NULL,", \n"));
		n->t_dwn_src = atoi(strtok(NULL,", \n"));
		n->t_dwn_src_type = atoi(strtok(NULL,", \n"));
		fgets(buff,255,infile);
		/* allocate memory for various Adaptrode types */
		if (n->adapt_cnt > 0) {
			n->synapses = 
			(Adaptrode *) malloc(sizeof(Adaptrode) * b->neurons[i].adapt_cnt);
			/* Read Adaptrodes */
			for (j=0; j<b->neurons[i].adapt_cnt; j++) {
				initAdaptrode(&(b->neurons[i].synapses[j]));
				a = &(b->neurons[i].synapses[j]);
				p = strtok(buff,", \n");
				a->id = atoi(p);
				a->atype_id = atoi(strtok(NULL,", \n"));
				a->type = atoi(strtok(NULL,", \n"));
				a->learn = atoi(strtok(NULL,", \n"));
/*
				a->extinguish = atoi(strtok(NULL,", \n"));
*/
				a->src_type = atoi(strtok(NULL,", \n"));
				a->src_id = atoi(strtok(NULL,", \n"));
				a->response = atof(strtok(NULL,", \n"));
				a->w_max = atof(strtok(NULL,", \n"));
				for(k=0; k<4; k++) a->w[k] =
					atof(strtok(NULL,", \n"));
				a->age = atol(strtok(NULL,", \n"));
				fgets(buff,255,infile);
			}
		}
	} /* end for each neuron loop */
	while ((buff[0] == '#') || (buff[0] == '\n')) {
		fgets(buff,255,infile); 
	}
	/* Get Outslots info */
	b->outslots = (Outslot *) malloc(sizeof(Outslot) * b->outslot_cnt);
	for (i=0; i<b->outslot_cnt; i++) {
		initOutslot(&(b->outslots[i]));
		p = strtok(buff, ", \n");
		b->outslots[i].id = atoi(p);
		b->outslots[i].greyscale = atoi(strtok(NULL,", \n"));
		b->outslots[i].value = atof(strtok(NULL,", \n"));
		b->outslots[i].neuron_id = atoi(strtok(NULL,", \n"));
		fgets(buff,255,infile);
	}
}

void saveBrain(Brain * b, FILE *hibfile)
{
	int i, j, k;
	Neuron * n;
	Adaptrode * a;

	if (!b || !hibfile) return;
	fprintf(hibfile,"# Brain Hibernation File\n");
	fprintf(hibfile,"%d, %d, %d, %d, %d\n", b->atype_cnt, b->ntype_cnt, b->inslot_cnt,
										b->neuron_cnt, b->outslot_cnt);
	/* save Atype info */
	fprintf(hibfile,"# Atype Information\n");
	for (i=0; i<b->atype_cnt; i++) {
		fprintf(hibfile,"%d, %lf, %lf, %lf, %lf", 
			b->atypes[i].id,
			b->atypes[i].decay,
			b->atypes[i].extinction,
			b->atypes[i].wt_max,
			b->atypes[i].wt_min);
		for (j=0; j<4; j++) {
			fprintf(hibfile,", %lf, %lf", b->atypes[i].alpha[j], 
				b->atypes[i].delta[j]);
		}
		fprintf(hibfile,"\n");
	}
	/* save Ntype info */
	fprintf(hibfile,"# Ntype Information\n");
	for (i=0; i<b->ntype_cnt; i++) {
		fprintf(hibfile,"%d, %d, %lf, %lf", 
			b->ntypes[i].id,
			b->ntypes[i].US_src,
			b->ntypes[i].t_decay,
			b->ntypes[i].t_alpha);
		fprintf(hibfile,"\n");
	}
	/* save Inslot Information */
	fprintf(hibfile,"# Inslot Information\n");
	for (i=0; i<b->inslot_cnt; i++) {
		fprintf(hibfile,"%d, %d, %lf\n", b->inslots[i].id,
			                             b->inslots[i].greyscale,
										 b->inslots[i].value);
	}
	/* save Neuron Information */
	fprintf(hibfile,"# Neuron Information\n");
	for (i=0; i<b->neuron_cnt; i++) {
		n = &(b->neurons[i]);
		fprintf(hibfile,"%d, %d, %lf, %lf, %lf, %lf, %lf, %d, %ld, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d\n",
			n->id, n->ntype_id, n->out0, n->out1, n->thresh, n->t_base, n->sum, 
			n->phase, n->age, n->adapt_cnt,
			n->reward_src, n->reward_src_type, n->punish_src, n->punish_src_type, 
			n->confirm_src, n->confirm_src_type, 
			n->t_up_src, n->t_up_src_type, 
			n->t_dwn_src, n->t_dwn_src_type);
		for (j=0; j<n->adapt_cnt; j++) {
			a = &(n->synapses[j]);
			fprintf(hibfile,"%d, %d, %d, %d, %d, %d, %lf, %lf", 
				a->id, a->atype_id, a->type,
				a->learn, a->src_type, a->src_id, a->response,
				a->w_max);
			for (k=0; k<4; k++) {
				fprintf(hibfile,", %lf", a->w[k]);
			}
			fprintf(hibfile,", %ld", a->age);
			fprintf(hibfile, "\n");
		}
	}
	/* save Outslot information */
	fprintf(hibfile,"# Outslot Information\n");
	for (i=0; i<b->outslot_cnt; i++) {
		fprintf(hibfile,"%d, %d, %lf, %d\n",
			b->outslots[i].id, b->outslots[i].greyscale,
			b->outslots[i].value, b->outslots[i].neuron_id);
	}
}

void makeBrain(Brain * b, int at, int nt, int in, int n, int a, int o )
{
/*
	This is a utility function to construct simple brains for testing
	purposes.  It can be used to create a skeleton network but each
	neuron will have the same number of adaptrodes.
*/
	int i, j;
	if (!b) return;

	initBrain(b);
	b->atype_cnt = at;
	b->atypes = (Atype *) malloc(sizeof(Atype) * b->atype_cnt);
	for (i=0; i<b->atype_cnt; i++) {
		initAtype(&(b->atypes[i]));
		b->atypes[i].id = i;
	}
	b->ntype_cnt = nt;
	b->ntypes = (Ntype *) malloc(sizeof(Ntype) * b->ntype_cnt);
	for (i=0; i<b->ntype_cnt; i++) {
		initNtype(&(b->ntypes[i]));
		b->ntypes[i].id = i;
	}
	b->inslot_cnt = in;
	b->inslots = (Inslot *) malloc(sizeof(Inslot) * b->inslot_cnt);
	for (i=0; i<b->inslot_cnt; i++) {
		initInslot(&(b->inslots[i]));
		b->inslots[i].id = i;
	}
	b->neuron_cnt = n;
	b->neurons = (Neuron *) malloc(sizeof(Neuron) * b->neuron_cnt);
	for (i=0; i<b->neuron_cnt; i++) {
		initNeuron(&(b->neurons[i]));
		b->neurons[i].id = i;
		b->neurons[i].adapt_cnt = a;
		b->neurons[i].synapses = 
			(Adaptrode *) malloc(sizeof(Adaptrode) * b->neurons[i].adapt_cnt);
			/* Read Adaptrodes */
		for (j=0; j<b->neurons[i].adapt_cnt; j++) {
			initAdaptrode(&(b->neurons[i].synapses[j]));
			b->neurons[i].synapses[j].id = j;
		}
	}
	b->outslot_cnt = o;
	b->outslots = (Outslot *) malloc(sizeof(Outslot) * b->outslot_cnt);
	for (i=0; i<b->outslot_cnt; i++) {
		initOutslot(&(b->outslots[i]));
		b->outslots[i].id = i;
	}
}

void processBrain(Brain * b, FILE *record)
{
/*
	This function is the main engine for processing the brain neural
	network (along with input and output slots).
	
	PRECONDITION: Brain has been read from a config or hib file and 
	              linked via the above function.  Also, the record FILE
				  must have been opened and initialized. 
	POSTCONDITION: Brain will be updated and appropriate records will have
	              been written to the record FILE.
*/
	int i, j, k;
	double wt[4], w, h[4];		/* store current state needed for computing
								   next state values  */
	Neuron *n = NULL;			/* convenience pointers */
	Adaptrode *a = NULL;
	Outslot *o = NULL;

	if (!b) return;
	/* process all inslots - compute double value (% of greyscale) */
	
	for (i=0; i<b->inslot_cnt; i++) {
		if (b->inslots[i].record && record != NULL) {
			/* if the record switch is set and a valid file is open
			   write out the contents of this for data analysis */
			fprintf(record,"I,%d,%d,%lf,",i,b->inslots[i].greyscale,
				b->inslots[i].value);
		}
		b->inslots[i].value = ((double)b->inslots[i].greyscale) / MAXGREYSCALE;
	}
	/* make a first pass through the neurons to shift output state */
	for (i=0; i<b->neuron_cnt; i++)
		b->neurons[i].out0 = b->neurons[i].out1;
	for (i=0; i<b->neuron_cnt; i++) {
		n = &(b->neurons[i]);
		if (n->record && record != NULL) {
			fprintf(record,"N,%d,%lf,%lf,%lf,%ld,",
				i, n->out0,n->thresh,n->sum,n->age);
		}
		n->sum = 0.0;						/* start with 0.0 value */
		/* for each adaptrode in the neuron */
		for (j=0; j<n->adapt_cnt; j++) {
			
			a = &(n->synapses[j]);
			
			/* set US value in h[1] */
			if (n->ntype->US_src == NONE) h[1] = 1.0; /* non-associative */
			else if (n->ntype->US_src == NEURON)	h[1] = n->out0;
			else h[1] = n->synapses[0].response;	/* typical assoc. */

			if (a->type != INHIBIT) {
				if (n->reward) h[2] = *(n->reward);
				else h[2] = 1.0;
			}
			else {
				if (n->punish) h[2] = *(n->punish);
				else h[2] = 1.0;
			}
			if (n->confirm) h[3] = *(n->confirm);
			else h[3] = 1.0;

			if (a->record && record != NULL) {
				fprintf(record,"A,%d,%lf,%lf,%lf,%lf,%lf,%ld,",
					j,a->response,a->w[0],a->w[1],a->w[2],a->w[3],a->age);
			}
			/* for each weight in the vector do */
			for (k=0; k<4; k++) wt[k] = a->w[k];
			
			if (a->type == FIXED) a->response = a->w[0];
			/* if there has been activity at the source then reset the
			   adaptrode response to the value in w[0], otherwise decay
			   the old response exponentially. */
			else if (*(a->src) > MINSIGNAL) {
				a->response = a->w[0];
			}
			else a->response = a->response * a->atype->decay;
			
			/* determine if the primary signal has arrived prior to the
			   arrival of the hurdle (US) signal.  If valid conditions
			   then enable learning (potentiation). */
			if (*(a->src) > MINSIGNAL && h[1] < LOWERBOUND) a->learn = 1;

			/* Modified 1/17/01, G. Mobus.  Added extinction computation */
			/* set up conditions for extinction. Extinction occurs if
			   the primary is active (stronly) but after a given time
			   the secondary (US) signal is not.  This condition will be
			   tested after extinguish has counted down from 
			   EXTINCT_DWN_CNT. */
			if (*(a->src) > STRONGSIGNAL && h[1] < LOWERBOUND && 
				a->extinguish == 0 && a->type != FIXED) 
							a->extinguish = EXTINCT_DWN_CNT;
			a->extinguish--;			/* down count */
			if (a->extinguish < 0) a->extinguish = 0; /* reset */
			if (a->extinguish == 1 && h[1] < LOWERBOUND && 
				a->type != FIXED) {
				printf("In Extinction\n");
				a->w[0] = a->w[0] - a->atype->extinction * (a->w[0]-wt[1]);
				a->response = 0.0;
				a->extinguish = 2;  /* check on each subsequent cycle */
			}
			/* Compute weight vector */
			else if (a->type != FIXED) {
				printf("In Normal\n");
				a->w[0] = a->w[0] + 
					*(a->src)*a->atype->alpha[0]*
					a->learn*(a->w_max - a->w[0]) -
					a->atype->delta[0]*(a->w[0] - a->w[1]);
				h[1] = h[1] * a->learn;  // switch off transfer to w1 level
				for (k=1; k<4; k++) {
					if (k == 3) w = a->atype->wt_min;
					else w = a->w[k+1];
					a->w[k] = a->w[k] +
						h[k] * a->atype->alpha[k] * (wt[k-1] - wt[k]) -
						a->atype->delta[k]*(wt[k] - w);
				}

				/* Test for nonlinear growth of w_max. */
				if (a->w[3] > NONLINEAR_THRESHOLD) {
					a->w_max = a->w_max + (1.0 - a->w_max)*a->w[3] -
						NONLINEAR_DECAY * (a->w_max- a->atype->wt_max);
				}
				else if (a->atype->alpha[3] > 0.0)
					a->w_max = a->w_max - NONLINEAR_DECAY * 
							   (a->w_max - a->atype->wt_max);
			}

			/* reset learn switch if conditions are met */
			if ((a->learn == 1) && (*(a->src) < MINSIGNAL)) a->learn = 0;

			/* compute dendritic integration. Note this is not a simple
			   summation as is typical for ANNs. Here the sum is shunted
			   by the difference between the sum and 1.0.  Also, inhibitory
			   signals are summed in at full weight under the theory that
			   inhibition is important! */
			if (a->type != INHIBIT) 
				n->sum = n->sum + (1.0 - n->sum)*a->response;
			else n->sum = n->sum - a->response;
			if (n->sum < 0.00000) n->sum = 0.0;

			a->age++;			/* increment and test age, reset if rolled
								   over to negative value */
			if (a->age <0L) a->age = 0L;
		}

		/* Axonal hillock threshold test */
		if (n->sum > n->thresh) n->out1 = n->sum;	/* experimental 
													   approximation */
		else n->out1 = n->out1 - OUTDECAY * n->out1;
	
		/* compute modulation on threshold if constants are non-zero */
		/* dynamic thresholds include those that increase as a function
		   of neuron output, effectively cutting off output, and those
		   that decrease with output, effectively bursting. */
		if (n->t_up != NULL) 
			n->thresh = n->thresh + (1.0 - n->thresh) * n->ntype->t_alpha * 
			    (*(n->t_up)) -
				n->ntype->t_decay * (n->thresh - n->t_base);
		if (n->t_dwn != NULL)
			n->thresh = n->thresh - n->thresh * n->ntype->t_alpha * 
			    (*(n->t_dwn)) +
				n->ntype->t_decay * (n->t_base - n->thresh);

		n->age++;			/* increment and test age */
		if (n->age < 0L) n->age = 0L;
	}

	/* for each outslot in the array */
	for (i=0; i<b->outslot_cnt; i++) {
		o = &(b->outslots[i]);
		if (o->record && record != NULL) {
			fprintf(record,"O,%d,%d,%lf,",i,o->greyscale,o->value);
		}
		o->value = b->neurons[o->neuron_id].out0; /* get from neuron */
		/* compute the greyscale for motor output */
		o->greyscale = (int)(o->value * MAXGREYSCALE + 0.5);
	}
	if (record != NULL) fprintf(record,"%d\n", 999);
}

/*
void main(void)
{
	FILE *fp = NULL;
	FILE *out = NULL;
	FILE *in = NULL;
	Brain brain;

	fp = fopen("brain.config", "r");
	if (fp) {
		initBrain(&brain);
		readBrain(&brain, fp);
	}
	fclose(fp); 
	
	linkBrain(&brain);

	out = fopen("brain.csv","w");
	in = fopen("experiment.csv","r");
	while (!feof(in)) {
		fscanf(in,"%d,%d,%d,%d,%d,%d\n",
			&brain.inslots[0].greyscale,
			&brain.inslots[1].greyscale,
			&brain.inslots[2].greyscale,
			&brain.inslots[3].greyscale,
			&brain.inslots[4].greyscale,
			&brain.inslots[5].greyscale);
		processBrain(&brain,out);
	}

	fclose(in);
	fclose(out);

	fp = fopen("brain.hib","w");
	if (fp) saveBrain(&brain, fp);
	fclose(fp);
}
*/

/* End of realbrain.c */