/*
 # mngclassify.c
 #
 # This is a NG post-classifier for sequences with 
 # fractal context representation.
 # 
 # STDIN delivers
 # 1st:  ALL neurons given in order
 # 2nd:  patterns to classify
 #
 # arg 1: number of neurons
 # arg 2: pattern dimension
 # arg 3: context eta (context metric influence)
 # arg 4: context factor for merge in linear combination (update)
 #
 #
 */



#include <stdio.h>
#include <stdlib.h>
#include <float.h>

#define MIX_CONTEXT_WEIGHT(NEURON,INDEX)\
 (mix_c_w * (NEURON) -> context[INDEX] + (1-mix_c_w) * (NEURON) -> coord[INDEX])

#define error(X) fprintf(stderr, (X)), exit(1)
#define panic(X) neuronsfree(), error((X))

#define inline __inline

/******************************************************************************/

typedef struct NEURON_TYPE *NEURON;

typedef struct NEURON_TYPE {
  int      idx;                   /* neuron index */
  double   dist;                  /* distance to pattern */
  double   activation;            /* activation for this neuron */
  double   *context;              /* representing compressed context */
  double   *coord;                /* codebook-coordinates */
} NEURON_TYPE;

NEURON *neurons,
       lastwinner;                /* for handling the last winner (context) */

double context_eta, mix_c_w;

int neurons_no;
int neurons_lastindex;

int pattern_dimension;            /* dimension of data set */

/******************************************************************************/



void neuron_free(NEURON n)
{
  free(n -> coord);
  free(n -> context);
  free(n);
}



void neuronsfree()
{

  while(neurons_lastindex >= 0)
    neuron_free(neurons[neurons_lastindex--]);

  free(neurons);

  neurons = NULL;

  neurons_lastindex = 0;
}



NEURON neuron_new_empty()
{

  NEURON n = (NEURON)malloc(sizeof(NEURON_TYPE));

  if(n == NULL) panic("neuron_new_empty(): cannot malloc neuron!");

  if(neurons_lastindex == 0 &&
      (neurons =
         (NEURON *)malloc(sizeof(NEURON) * (neurons_no+1))) == NULL)

      panic("neuron_new_empty(): cannot malloc neurons!");


  n -> idx = neurons_lastindex;

  neurons[neurons_lastindex++] = n; /* XXX: very courageous here */

  return n;
}



NEURON neuron_new(void)
{

  NEURON n = neuron_new_empty();

  if((n -> coord = (double *)malloc(sizeof(double) * pattern_dimension)) == NULL)
    panic("neuron_new(): cannot malloc coord!");

  if((n -> context = (double *)malloc(sizeof(double) * pattern_dimension)) == NULL)
    panic("neuron_new(): cannot malloc context!");

  n -> dist = 0.;
  n -> activation = 0.;

  return n;
}


inline double neuron_patterndistance(NEURON n, double *pattern)
{

  int i = pattern_dimension;

  double *w = n -> coord, res = 0., tmp;

  while(--i >= 0) tmp = w[i] - pattern[i], res += tmp * tmp;

  return res;
}



inline double neuron_contextdistance(NEURON n, NEURON l)
{

  int i = pattern_dimension;

  double *c = n -> context, res = 0., tmp;

  while(--i >= 0) tmp = c[i] - MIX_CONTEXT_WEIGHT(l,i), res += tmp * tmp;

  return res;
}


void getwinner(double *pattern, NEURON *result)
{

  double min = DBL_MAX, d;

  int i;

  NEURON n;


  for(i = 0; i < neurons_no;  i++) {

    n = neurons[i];

    if(n == lastwinner || n -> idx < 0) continue;  /* postsynaptic inhibition added: 26.11.03 */

    n -> dist = d = (1. - context_eta) *  neuron_patterndistance(n, pattern) +
                          context_eta  * neuron_contextdistance(n, lastwinner);

    if(d < min) {
       min = d;
       *result = n;
    }
  }
}



void neuron_stdin(void)
{

  NEURON n = NULL;

  int i, p=0;

  scanf("%d", &i);

  n = neurons[i];
  n -> idx = i;

  for(i = 0; i < pattern_dimension; i++) {
    scanf("%lf", n -> coord + i);
    if(n -> coord[i] != 0.) ++p;
  }

  for(i = 0; i < pattern_dimension; i++) {
    scanf("%lf", n -> context + i);
    if(n -> context[i] != 0.) ++p;
  }
  if(p==0) n -> idx = -1;

}



/******************************************************************************/



/* get next input vector from standard input */
/* XXX result vector allocated but never freed */

double * pattern_stdin(void)
{

  static double *res = NULL;

  int column;

  if(res == NULL) {

    res = (double *)malloc(pattern_dimension * sizeof(double));

    if(res == NULL)
      panic("pattern_stdin(): Trouble allocating result vector!");

  }

  if(1 != scanf("%lf", res)) return NULL;

  if(*res <= -999.99) {
    printf("%g\n", *res);
    lastwinner = neurons[neurons_no];        /* reset context */
    if(1 != scanf("%lf", res)) return NULL;  /* read next symbol */
  }

  for(column = 1; column < pattern_dimension; column++)
    if(1 != scanf("%lf", res+column))
      return NULL;

  return res;
}



void neurons_stdin(void)
{

  int p, i;

  NEURON neuron;


  for(p = 0; p <= neurons_no; p++) { /* == neurons_no => lastwinner default */

    neuron = neuron_new();

    neuron -> idx = -1;

    for(i = 0; i < pattern_dimension; i++)
      neuron -> coord[i] = neuron -> context[i] = 0.;
  }
  lastwinner = neurons[neurons_no];

  for(p = 0; p < neurons_no; p++)
    neuron_stdin();

}


/******************************************************************************/
/* no much checking done for main argument evaluation                         */
/******************************************************************************/


int main(int argc, char **argv)
{

  NEURON winner;

  double *pat;

  neurons_no = atoi(argv[1]);
  pattern_dimension = atoi(argv[2]);
  context_eta = (argc >= 4) ? atof(argv[3]) : .5;
  mix_c_w = (argc >= 5) ? atof(argv[4]) : .5;


  neurons_stdin();


  while((pat = pattern_stdin()) != NULL) {

    getwinner(pat, &winner);

    lastwinner = winner;

    printf("%d\n", lastwinner -> idx);
  }


  return 0;
}
