/* 
    Neural Network Simulator

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    as published by the Free Software Foundation; either version 2
    of the License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

    (C) 2006 Jason Hunt
    nulluser@gmail.gom 
*/


#include <stdio.h>
#include <malloc.h>
#include <math.h>

#include "console.h"
#include "neuron.h"


/* Compute values for all neurons */
void process_neurons( neuron_type *n )
{
    // Point to first neuron in linked list
    neuron_type *tn = n;
    
    // Calculate next output for all neurons
    while (tn != NULL)
    {
        double sum = 0;
       // double prod = 1;
                
        // Compute sums and products for sources
        for (int j = 0; j < tn->num_sources; j++)
        {                        
            // Compute input transport function 
            
            double source_val = //tn->source_bias[j] +
                                tn->source_scaler[j]  * *tn->sources[j];// * 
                              //  pow(*tn->sources[j], tn->source_power[j]);
            
            // Adjust sum and product
            sum += source_val;
          //  prod *= source_val;                        
        }

        // COmpute next values based on all weights and sources
        tn->next_val = tanh(sum * tn->sum_weight +
                    //  prod * tn->prod_weight +
                      
                    // tn->derivative * tn->derv_weight +
                   //   tn->integral* tn->intr_weight +                                            
                     // tn->last_val * tn->last_weight +                       
                    
                      tn->bias
                      );

        // Compute discrete intergral
       // tn->integral += (tn->out + tn->next_val) / 2.0;

        // Compute discrete derivative 
        //tn->derivative = tn->next_val - tn->out;

        tn = tn->next;        // Advance to next neuron
    }
    
    // Start with first neuron
    tn = n;
    
    // Latch the new values for all of the neurons
    //for (int i = 0; i < num_neurons; i++)
    while (tn != NULL)
    {
       // tn->last_val = tn->out;       // Save last value
        tn->out = tn->next_val;       // Set new value
        tn = tn->next;                // Point to next neuron
    }
}
/* End of process neurons */


/* Return a point to a neuron be ID. Return NULL if not found */
neuron_type * get_neuron(neuron_type *n, unsigned long id)
{
    // Test each neuron 
    while (n != NULL)
    {
        if (n->id == id) return(n);    // ID match
        
        n = n->next;                    // Next neuron in list
    }    
    
    return(NULL);                        // Not found
}
/* End of get_neuron */



/* Connect a neuron to a double source */
void neuron_connect(neuron_type *n, unsigned id, double *source, 
            double bias, double scaler, int power)
{
    if (source == NULL) return;

    neuron_type *tn = get_neuron(n, id);
    
    if (tn == NULL) return;

    if (tn->num_sources >= MAX_SOURCES) return;
    
    // First unused source slot
    unsigned int s = tn->num_sources; 
    
    // Connect source pointer to the outout of n2
    tn->sources[s] = source;
    
    tn->source_bias[s] = bias;
    tn->source_scaler[s] = scaler;
    tn->source_power[s] = power;
    
    tn->num_sources++;
}
/* End of connect */

/* Add a neuron to the linked list */
void add_neuron(neuron_type **n, unsigned long id, 
                double bias, double sum, double prod, 
                double intr, double derv, double feedback)
{
    if (get_neuron(*n, id) != NULL)
    {
        write_console("Neuron ID in use\n");
        return;
    }

    neuron_type *new_neuron = (neuron_type *) malloc(sizeof(neuron_type));
                
    if (new_neuron == NULL)
    {
        write_console("Unable to get neuron memory \n");
        while(1);
    }
        
    new_neuron->id = id;

    new_neuron->sum_weight = sum;
    new_neuron->prod_weight = prod;
    new_neuron->derv_weight = derv;
    new_neuron->intr_weight = intr;

    new_neuron->feedback_weight = feedback;

    new_neuron->bias = bias;
    new_neuron->out = 0;
    
    
    new_neuron->derivative = 0;
    new_neuron->integral = 0;

    new_neuron->last_val = 0;
    new_neuron->num_sources = 0;
    
    // Insert into list 
    neuron_type *tmp = *n;  // Save origional root neuron
    
    *n = new_neuron;        // Point root to the new neuron

    new_neuron->next = tmp; // point the new neuron to the origional root
}
/* End of add neuron */


/* Show entire neuron list */
void show_neuron(neuron_type *n, unsigned long id)
{
    neuron_type *tn = get_neuron(n, id);
    
    if (tn == NULL) return;

    char buff[400];

    sprintf(buff, " Neuron ID: %d\n", id);
    write_console(buff);
    
    for (int i = 0; i < tn->num_sources; i++)
    {
        sprintf(buff, " Source %8.5f", *tn->sources[i]);
        write_console(buff);

        sprintf(buff, "   Bias: %8.5f",  tn->source_bias[i]);
        write_console(buff);

        sprintf(buff, "  Scaler: %8.5f", tn->source_scaler[i]);
        write_console(buff);

        sprintf(buff, "  Power:  %2d\n", tn->source_power[i]);
        write_console(buff);        
    }
            
    sprintf(buff, "  Sum weight:    %8.5f\n", tn->sum_weight);
    write_console(buff);        

    sprintf(buff, "  Prod weight:   %8.5f\n", tn->prod_weight);
    write_console(buff);        

    sprintf(buff, "  Last weight:   %8.5f\n", tn->last_weight);
    write_console(buff);        

    sprintf(buff, "  Bias:          %8.5f\n", tn->bias);
    write_console(buff);        

    sprintf(buff, "  Out:           %8.5f\n\n", tn->out); 
    write_console(buff);   
}
/* End of show_neuron */


/* Free entire neuron list */
void free_neuron_list(neuron_type *n)
{
    neuron_type *tmp;
        
    while (n != NULL) 
    {
        tmp = n->next;
        free(n);
        n = tmp;
    }    
}
/* End of free neuron list */




