#pragma warning (disable:4786)
#include "ai nnet.hpp"
#include <assert.h>
#include <math.h>

Back_Layer::Back_Layer (int nodes,int ninputs,bool first)
  {
    Create (nodes,ninputs,first);
  }

void Back_Layer::Create (int nodes,int ninputs,bool first)
  {
     int i,j;
     double temp;

     next = NULL;
     prev = NULL;
     num_nodes = nodes;
     next = prev = NULL;
     thenodes = (Back_Node *) calloc (sizeof (Back_Node),num_nodes);
     num_inputs = ninputs;
     inputs = (double *) calloc (sizeof (double),num_inputs);
     outputs = (double *) calloc (sizeof (double),num_nodes);
     errors = (double *) calloc (sizeof (double),num_nodes);
     for (i=0;i<num_inputs;i++)
       {  inputs[i] = 0;
       }
     for (i=0;i<num_nodes;i++)
       {
          thenodes[i].weights = (double *) calloc (sizeof (double),num_inputs);
          thenodes[i].deltas = (double *) calloc (sizeof (double),num_inputs);
          for (j=0;j<num_inputs;j++)
            {
               thenodes[i].deltas[j] = 0.0;
               if (!first)
                 {
                    temp = rand ();
                    temp /= ((double) RAND_MAX);
                    temp = temp - 0.5;
                    thenodes[i].weights[j] = temp;
                 }
               else
                 {
                    thenodes[i].weights[j] = 1.0;
                 }
            }
       }
  }

Back_Layer::~Back_Layer (void)
  {
    Destroy ();
  }

void Back_Layer::Destroy (void)
  {
     int i;

     for (i=0;i<num_nodes;i++)
       {
          free (thenodes[i].weights);
          free (thenodes[i].deltas);
       }
     free (thenodes);
     free (inputs);
     free (errors);
  }

void Back_Layer::propagate_forward (void)
  {
     int i,j;
     double sum;

     assert ((!next) || (num_nodes == next->num_inputs));
     for (i=0;i<num_nodes;i++)
       {
          if (!prev)
            outputs[i] = inputs[i];
          else
            {
               sum = 0.0;
               for (j=0;j<num_inputs;j++)
                 {  sum += (inputs[j]*thenodes[i].weights[j]);
                 }
               outputs[i] = 1.0/(1.0+exp (-1.0*sum));
            }
          if (next)
            next->inputs[i] = outputs[i];
       }
  }

void Back_Layer::propagate_error (void)
  {
     int i,j;

     assert (next);
     for (i=0;i<num_nodes;i++)
       {
          errors[i] = 0;
          for (j=0;j<next->num_nodes;j++)
            {
               errors[i] += (next->errors[j]*next->thenodes[j].weights[i]);
            }
          errors[i] *= (outputs[i] * (1.0-outputs[i]));
       }
  }

void Back_Network::correct (bool finetune)
  {
     compute_error (finetune);
     propagate_error ();
     adjust_weights ();
  }

void Back_Network::adjust_weights (void)
  {
     int i,j,k;
     double tochange;

     for (i=1;i<num_layers;i++)
       {
          for (j=0;j<thelayers[i].num_nodes;j++)
            {
               for (k=0;k<thelayers[i].num_inputs;k++)
                 {
                    tochange = thelayers[i].inputs[k]*eta*thelayers[i].errors[j];
                    tochange += (alpha*thelayers[i].thenodes[j].deltas[k]);
                    thelayers[i].thenodes[j].deltas[k] = tochange;
                    thelayers[i].thenodes[j].weights[k] += tochange;
                 }
            }
       }
  }

void Back_Network::propagate_error (void)
  {
     int i;

      for (i=(num_layers-2);i>=0;i--)
        {
           thelayers[i].propagate_error ();
        }
  }

void Back_Network::compute_error (bool finetune)
  {
     int i;

     for (i=0;i<num_outputs;i++)
       {
          if (!finetune)
            thelayers[num_layers-1].errors[i] = (wanted_output[i] - output[i]);
          else
            {
               thelayers[num_layers-1].errors[i] = output[i];
               thelayers[num_layers-1].errors[i] *= (1.0 - output[i]);
               thelayers[num_layers-1].errors[i] *= (wanted_output[i] - output[i]);
            }
       }
  }

// This function does not work, the net will not stabalize to a solution
void Back_Network::Learn (bool iscorrect,bool finetune,double error)
{
	int i;
	double *temp;

	if (iscorrect)
	{
		temp = get_answer ();
		for (i=0;i<thelayers[num_layers-1].num_nodes;i++)
		{
			if (temp[i] > 0.5)
				wanted_output[i] = 1.0;
			else
				wanted_output[i] = 0.0;
		}
		correct (finetune);
	}
	else
	{
		temp = get_answer ();
		for (i=0;i<thelayers[num_layers-1].num_nodes;i++)
		{
			if ((rand ()%100) < error)
			{
				if (temp[i] > 0.5)
					wanted_output[i] = 1.0;
				else
					wanted_output[i] = 0.0;
			}
			else
			{
				if ((rand ()&0x01))
					wanted_output[i] = 1.0;
				else
					wanted_output[i] = 0.0;
			}
		}
	    correct (finetune);
	}
}

Back_Network::Back_Network (int layers,int *num_nodes,double neta,double nalpha)
  {
     int i;

     eta = neta;
     alpha = nalpha;

     num_inputs = num_nodes[0];
     num_outputs = num_nodes[layers-1];
     input = (double *) calloc (sizeof (double),num_inputs);
     output = (double *) calloc (sizeof (double),num_outputs);
     wanted_output = (double *) calloc (sizeof (double),num_outputs);
     num_layers = layers;
     thelayers = (Back_Layer *) calloc (sizeof (Back_Layer),layers);
     thelayers[0].Create (num_nodes[0],num_nodes[0],true);
     thelayers[0].next = &(thelayers[1]);
     for (i=1;i<layers;i++)
       {
          thelayers[i].Create (num_nodes[i],num_nodes[i-1],false);
          thelayers[i].prev = &(thelayers[i-1]);
          thelayers[i].next = &(thelayers[i+1]);
       }
     thelayers[layers-1].next = NULL;
  }

Back_Network::~Back_Network (void)
  {
     int i;
     for (i=0;i<num_layers;i++)
       {
          thelayers[i].Destroy ();
       }
     free (thelayers);
  }

void Back_Network::propagate_forward (double *ninputs)
  {
     int i;

     for (i=0;i<num_inputs;i++)
       {
         input[i] = ninputs[i];
         thelayers[0].inputs[i] = input[i];
       }
     for (i=0;i<num_layers;i++)
       {
          if (i != (num_layers-1))
            assert (thelayers[i].num_nodes == thelayers[i+1].num_inputs);
          thelayers[i].propagate_forward ();
       }
     for (i=0;i<num_outputs;i++)
       {
         output[i] = thelayers[num_layers-1].outputs[i];
       }
  }
