/* 
 * Thomas Pantzer: neuro_gas.cc, v0.1b  23.9.97 22:29 
 * Thomas Pantzer: neuro_gas.cc, v0.4.7  8.6.99 16:32 
 * 
 * 
 * Copyright 1997 by Thomas Pantzer
 * 
 * Permission to use, copy, modify, and distribute this software for noncommercial 
 * use and without fee is hereby granted, provided that the above copyright notice 
 * appear in all copies and that both that copyright notice and this permission 
 * notice appear in supporting documentation, and that the name of the Author not be 
 * used in advertising or publicity pertaining to distribution of the software 
 * without specific, written prior permission. The Author makes no representations about
 * the suitability of this software for any purpose.  It is provided "as is" 
 * without expressed or implied warranty. 
 * 
 * 
 * pantec@aix520.informatik.uni-leipzig.de   (Thomas Pantzer) 
 * 
 */


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

#include "copyright.h"

#include "neuro_gas.h"
#include "neuron_show.h"
#include "trigonom.h"

void t_SOM::Init(int x, int y)
{
//  t_Net::Init(Robot);
  Size.x = x;
  Size.y = y;
  age = 0;
  N = 0;

  add_new = false; 
  actual = new t_Neuron;
  actual->Init(0,0,0,ROOT,0);
  root = actual;
  last_dist = 0.0;

//  Create(24,24);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// please satisfy the following conditions for a well centered net image
// w / ((x+1) * 2) >= 1
// w % ((x+1) * 2) == 0
// h / ((y+1) * 2) >= 1
// h % ((y+1) * 2) == 0       
//
// example: x==79 w==640   y==59 h==480
//______________________________________________________________________
void t_SOM::Create(int x, int y)
{
   t_Neuron *prev_y,*prev_x;
   
//   int Sx,Ox,Sy,Oy;
   int n_cnt;
   
   prev_x = NULL;	
   
   Cnt.x = x;
   Cnt.y = y;
      
//   Sx = (int)(size.x / (x+1));
//   Sy = (int)(size.y / (y+1));
//   Ox = Sx / 2;
//   Oy = Sy / 2;
   

   n_cnt = 0;

   for (int i=1; i<x; i++)
     {
      for (int j=1; j<y; j++)
        {
         actual->next = new t_Neuron;
         actual->next->Init(n_cnt,(double)i/(double)x,(double)j/(double)y,_HIDDEN_N,0);
         n_cnt++; 
      
         prev_y = actual;      
         actual = actual->next;

         if ((j == 1) && (i == 1))
            prev_x = actual;
	  else  
	    prev_x = NULL;
         
         if (j>1)
            actual->Connect(prev_y,0.0);
            
         if ((i>1) && (prev_x != NULL))
            {
             actual->Connect(prev_x,0.0);   
             prev_x = prev_x->next;
            } 
        } 
     }
}


void t_SOM::CalcSize(void)
{
   double minx,miny,maxx,maxy;
   int i;
   
   minx = 1000000000.0;
   maxx = -1000000000.0;
   miny = 1000000000.0;
   maxy = -1000000000.0;
 
   actual = root->next;
     
   i = 0;
   while (actual != NULL)
     {
      if (minx > actual->pos.x)
         minx = actual->pos.x;

      if (miny > actual->pos.y)
         miny = actual->pos.y;

      if (maxx < actual->pos.x)
         maxx = actual->pos.x;

      if (maxy < actual->pos.y)
         maxy = actual->pos.y;
         
      actual = actual->next;   
      i++;
     }	
     
     
   if (i > 0)
      {
       Size.x = maxx - minx;
       Size.y = maxy - miny;
       Anchor.x = minx;
       Anchor.y = miny;
       
       fprintf(stderr,"t_SOM::Size(): (%g,%g)\n",Size.x,Size.y);
      }  
}



t_Neuron *t_SOM::Add(t_vector *pos)
{
   actual = root;

   while (actual->next != NULL)
      actual = actual->next;

   actual->next = new t_Neuron;

   N++;
   actual->next->Init(N,pos->x,pos->y,_HIDDEN_N,0);
   

   fprintf(stderr,"add t_SOM_neuron: %d\n",N);
   
   return actual->next;
}

char t_SOM::Update(t_vector *pos)
{
   double diff = 0.0;
   t_Neuron *neuron,*W;
   
   W = GetWinner(pos,true);

   switch (N)
     {
      case 0:
        neuron = Add(pos);           
        return false;
        break;
        
      case 1:  
        diff = fabs(W->act_val - last_dist);
        if (diff < last_diff/3.0)
           {
            neuron = Add(pos);           
            W->Connect(neuron,0.0);
            last_winner = neuron;
            return true;
           } 
        break;
        
      default:
        if (last_winner != W)
           {
            neuron = Add(pos);           
            W->Connect(neuron,0.0);
            last_winner = neuron;
           } 
        break;     
     }
   

   if (N > 6)
      {
       W->time_stamp++; 
           
       W->pos.x = ((W->pos.x*W->time_stamp) + pos->x) / (W->time_stamp +1);
       W->pos.y = ((W->pos.y*W->time_stamp) + pos->y) / (W->time_stamp +1);
           
#ifdef DEBUG
       fprintf(stderr,"pos()\n",pos->x,pos->y);
#endif           
      }

/*
   if (N > 4)
      {
       pos->x = W->pos.x;
       pos->y = W->pos.y;
      }
 */  
   last_dist = W->act_val;
   last_diff = diff;
   last_winner = W;
/*
   if (W != NULL)        
 */           
   return true;
}







void t_SOM::Show(void)
{
  t_vector npos,apos;
  t_Synapse *Synapse;
     
  if ((root == NULL) || (root->next == NULL))
     return;
   
  CalcSize();    
  
  actual = root->next;
  
  while (actual != NULL)
    {
     npos.x = (actual->pos.x - Anchor.x) / Size.x;
     npos.y = (actual->pos.y - Anchor.y) / Size.y;
  
#ifdef DEBUG
     fprintf(stderr,"(%g,%g)\n",npos.x,npos.y);
#endif
  
     Show_Neuron(npos,actual->id,actual->act_val,actual->kind);      

     Synapse = actual->Synapse;
     while (Synapse != NULL)
       {
        apos.x = (Synapse->Axon->pos.x - Anchor.x) / Size.x;
        apos.y = (Synapse->Axon->pos.y - Anchor.y) / Size.y;

        Show_Synapse(npos,apos,Synapse->id,Synapse->Weight/*,Synapse->kind*/);      
        
        Synapse = Synapse->next;
       } 
       
     actual = actual->next;    
    } 
}


void t_SOM::Done(void)
{
  if (root != NULL)
     root->Done();
}




t_Neuron *t_SOM::GetWinner(t_vector *v,int calc)
{
  t_Neuron *Winner;
  double min;
  
  min = 1000000.0;	// only a real big number
  actual = root->next;
  Winner = actual;

	
  while (actual != NULL)
    {
//     if (actual->time_stamp == false)
//        {
         if ((calc == true) && (v != NULL))
            actual->act_val = v->Distance(&actual->pos);

         if (actual->act_val < min)
            {
#ifdef DEBUG
//             fprintf(stderr,"distance: %g\n",actual->act_val);
#endif          
             min = actual->act_val;
             Winner = actual;
            }
//        }            
    
     actual = actual->next;
    }

  return Winner;
}



void t_SOM::ResetLinks(t_Neuron *N)
{
  t_Synapse *S;
  
  S = N->Synapse;

  while (S != NULL)
    {
     S->Weight = 0.0;
     S = S->next;
    }
}




void t_SOM::ProcessInput(t_vector *v,int max_age)
{
  t_Neuron *Winner[24];
  t_Synapse *sp,*sdp;
//  t_vector nv,sv,tv;
  double neighbour,eps,d,g,dx,dy;
//  int j;
  
  age++;

  actual = root->next;

  while (actual != NULL)
    {
     sp = actual->Synapse;
     actual->time_stamp = false;
     
     while (sp)
       {
        sdp = sp;
        sp->Weight += 1.0;		// increase the connection time
        
        sp = sp->next;
        
//        if (sdp->Weight > max_age)
//           actual->Cut(sdp);		
       }
       
     actual = actual->next;
    }
  
  eps = 0.21;
  neighbour = 1.2;

  Winner[0] = GetWinner(v,true);
  d = Winner[0]->act_val;
     
  g = gaussian(d,neighbour,eps);

  dx = (v->x - Winner[0]->pos.x) * g; 
  dy = (v->y - Winner[0]->pos.y) * g;
  
  Winner[0]->pos.x += dx;
  Winner[0]->pos.y += dy;
  
  ResetLinks(Winner[0]);
  
#ifdef DEBUG
  fprintf(stderr,"Winner[%d]->pos(%g,%g), v=(%g,%g), distance=%g, learn.term:%g \n",
       Winner[0]->id,Winner[0]->pos.x,Winner[0]->pos.y,
                     v->x,v->y,Winner[0]->act_val,g); 
#endif    
}









/**/
/**/
/**/
/**/
/**/
/**/
/**/
/**/
/*****  End Of File  ****************************/
