#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#include "config.h"
#include "mlp.h"
#include "mlp_train.h"
#include "cell.h"
#include "KD_train.h"
#include "matrix.h"
#include "dtw.h"
#include "classifier.h"
#include "list.h"
#include "somp.h"
#include "dictionary.h"
#include "grad_desc_mod.h"

#include <math.h>
#include <time.h>
#include "simulated_annealing.h"
#include "signal_processing.h"
#include "detector.h"

#define HIDEN_NEURONS 30
#define OUTPUT_NEURONS 8

matrix *mfcc_features1(matrix *signal, unsigned int win, unsigned int inc, unsigned int fs)
{
  unsigned int FS_MIN = 500;
  unsigned int FS_MAX = 3000;

  matrix *frames =sp_enframe(signal, win, inc, SP_DONT_ADD_NULL);
  matrix *features = sp_mfcc(frames, 16, FS_MIN, FS_MAX, fs);
  matrix_free(frames);

  return features;
}

matrix *mfcc_features2(matrix *signal, unsigned int win, unsigned int inc, unsigned int fs)
{
  unsigned int FS_MIN = 500;
  unsigned int FS_MAX = 3000;
  unsigned int M = 2;

  list *L = list_alloc();
  matrix *frames =sp_enframe(signal, win, inc, SP_DONT_ADD_NULL);

  matrix *mfcc_coef = sp_mfcc(frames, 16, FS_MIN, FS_MAX, fs);
  list_add_last(L, element_alloc_matrix(mfcc_coef));

  matrix *deltas_mfcc_coef = sp_deltas(mfcc_coef, M);
  list_add_last(L, element_alloc_matrix(deltas_mfcc_coef));

  matrix *frame_energy = sp_signal_log_energy(frames);
  list_add_last(L, element_alloc_matrix(frame_energy));

  matrix *deltas_frame_energy = sp_deltas(frame_energy, M);
  list_add_last(L, element_alloc_matrix(deltas_frame_energy));

  matrix *features = matrix_cat_list(L, MATRIX_CAT_VERTICALLY);
  list_free(L);

  return features;
}

matrix *lpcc_features1(matrix *signal, unsigned int win, unsigned int inc, unsigned int fs)
{

  matrix *frames =sp_enframe(signal, win, inc, SP_DONT_ADD_NULL);
  matrix *features = sp_lpc_cepstrum(frames, 16, 16);
  matrix_free(frames);

  return features;
}

matrix *lpcc_features2(matrix *signal, unsigned int win, unsigned int inc, unsigned int fs)
{
  unsigned int M = 2;
  
  list *L = list_alloc();
  matrix *frames =sp_enframe(signal, win, inc, SP_DONT_ADD_NULL);

  matrix *lpcc_coef = sp_lpc_cepstrum(frames, 16, 16);
  list_add_last(L, element_alloc_matrix(lpcc_coef));

  matrix *deltas_lpcc_coef = sp_deltas(lpcc_coef, M);
  list_add_last(L, element_alloc_matrix(deltas_lpcc_coef));

  matrix *frame_energy = sp_signal_log_energy(frames);
  list_add_last(L, element_alloc_matrix(frame_energy));

  matrix *deltas_frame_energy = sp_deltas(frame_energy, M);
  list_add_last(L, element_alloc_matrix(deltas_frame_energy));

  matrix *features = matrix_cat_list(L, MATRIX_CAT_VERTICALLY);
  list_free(L);

  return features;
}


void determine_treshold(const char *sensFile, double (*trFunc)(matrix *, unsigned int , unsigned int , unsigned int ), const char *dictFile, char *word, const char *netFile)
{

  unsigned int i, n, dn;
  char trFile[300];
  
  //dictionary *dict = dictionary_load(dictFile);
  sentence **dictSens = sentences_load(dictFile, &dn, 1000);
  sentence **sens = sentences_load(sensFile, &n, 1000);
  sentence_comp_features2(dictSens, dn, trFunc);
  sentence_comp_features2(sens, n, trFunc);
  
  list *L = list_alloc();
  list_add_last(L, element_alloc_string(word));
  dictionary *d = dictionary_create(dictSens, dn);
  dictionary *dict = dictionary_filter_words(d, L);

  if (netFile!=NULL) {
    mlp *net = mlp_load(netFile);
    dictionary_transform_mlp(dict, net);
    sentence_mlp_transform2(sens, n, net);
  }

  //matrix **avs = dictionary_stat_norm(DICT);dictionary_stat_norm_aply(DICT, avs[0], avs[1]);
  //matrix **BR = dictionary_somp_basis(DICT, 6);dictionary_somp_projection(DICT, BR[0]);
  //matrix **avs = dictionary_stat_norm(DICT);dictionary_stat_norm_aply(DICT, avs[0], avs[1]);
  //puts("Basis");matrix_print(BR[0]);
  
  //matrix **avs = sentence_stat_norm(sens, n);sentence_stat_norm_aply2(sens, n, avs[0], avs[1]);dictionary_stat_norm_aply(dict, avs[0], avs[1]);
  //sentence_stat_norm_aply2(sens, n, avs[0], avs[1]);
  //sentence_somp_projection2(s
  //sentence_stat_norm_aply2(sens, n, avs[0], avs[1]);
  //matrix_print(matrix_submatrix(sens[0]->features, 0, sens[0]->features->m-1, 30, 50));

  detector_list_opt opt;
  opt.min_frames = 5;
  opt.max_frames = 40;
  matrix *weight = dictionary_weight_design(dict, &opt);

  puts("Vypocet prubehu v jednotlivych vetach");
  matrix **process = detector_word_process_2(sens, n, dict, &opt, 1, 4, weight);
  

  //matrix_print(matrix_transpose(process[0]));
  //matrix_print(matrix_transpose(process[1]));
  //matrix_print(matrix_transpose(process[2]));
  //sentence_print_info(sens[2]);
  //dictionary_print(dict);

  //matrix *ROC = detector_ROC(process, sens, n, dict, 0.25, 0.5, 500);
  //matrix_print(ROC);


  //matrix *dw = detector_word_detect(process[2], dict, treshold);
  //matrix_print(matrix_transpose(dw));
  ///cell_print(wp);
  
  //list_print(L[0]);
  //detector_WER(process[2], sens[2], dict, 0.25, &I, &S, &D, &N);

  //detector_ROC(process, sens, n, dict, 0.2, 50);

  matrix *trMin = matrix_alloc(1, dict->n);
  matrix *trMax = matrix_alloc(1, dict->n);
  for (i = 0; i<dict->n; i++) {
    matrix_set(trMin, 0, i, 0);
    matrix_set(trMax, 0, i, 10);
  }
  
  matrix *treshold = detector_treshold_try(process, sens, n, trMin, trMax, dict, 1000, 0.25);
  puts("Treshold");
  matrix_print(treshold);
  //matrix_save(treshold, trFile);

  for (i = 0; i<n; i++) {
    //matrix_print(matrix_transpose(process[i]));sentence_print_info(sens[i]);
  }

  puts("ISDN");
  matrix *ISDN = detector_quick_WER_2(process, sens, n, dict, treshold, 0.25);
  matrix_print(ISDN);
  printf("WER = %f\n", detector_WER(ISDN));
  matrix_free(ISDN);
  printf("slov ve slovniku : %i\n", dict->classes[0]->n);
}

double auc_energy (void *data)
{
  void **DATA = (void**)data;
  mlp *net = (mlp*)DATA[0];
  cell *trp = (cell*)DATA[1];
  double b = *((double *) DATA[2]);
  double E = (KD_train_energy_paralel(trp, net, b)/trp->n);
  return E;
}


void train_NN_SA(const char *log, const char *dictFile, const char *netFile, double b)
{
  srand ( time(NULL) );
  
  puts("Vytvareni treningovych dvojic");
  dictionary *dict = dictionary_load(dictFile);
/*
  matrix **avs = dictionary_stat_norm(dict);
  dictionary_stat_norm_aply(dict, avs[0], avs[1]);
  char str[100];
  str[0] = NULL;strcat(str, dictFile);strcat(str, "_AVARGE");
  matrix_save(avs[0], str);
  str[0] = NULL;strcat(str, dictFile);strcat(str, "_STDEV");
  matrix_save(avs[1], str);
//*/
  cell *train_pairs = KD_train_create_train_pairs3(dict, dict, 0);

  unsigned int inputs = matrix_size_m(element_get_matrix(cell_get(train_pairs, 0, 0)));
  unsigned int n[3];  n[0] = inputs; n[1] = HIDEN_NEURONS; n[2] = OUTPUT_NEURONS;
  mlp * net = mlp_alloc(3, n);
  mlp_weight_init(net, 0.00);
  
  sa_param *params = sa_param_alloc(mlp_get_total_connections(net));
  unsigned int i, j, k, pi;
  pi = 0;
  for (i = 0; i<mlp_get_num_layers(net)-1; i++) {
    for (j = 0; j<=mlp_get_neurons(net, i); j++) {
      for (k = 0; k<mlp_get_neurons(net, i+1); k++) {
        sa_param_define(params, pi, &(net->weights[i][j][k]));
        sa_param_dmax_set(params, pi, 1);
        //sa_param_dmax_set(params, pi, 1/((double)mlp_get_neurons(net, i)));
        pi++;
      }
    }
  }

  double B = b;
  void **data = malloc(sizeof(void*)*3);
  data[0] = (void*)net;
  data[1] = (void*)train_pairs;
  data[2] = (void*)&B;
  
  sa_algo SA;
  SA.dmax = 0.04;
  SA.Tmax = 0.001;
  SA.Tmin = SA.Tmax/200;
  SA.alfa = 0.05;
  SA.beta = 0.002;
  SA.kmax = 10;
  SA.energy = auc_energy;
  SA.data = data;
  SA.logfile = log;

  printf("template-based AUC = %f\n", KD_train_auc2(train_pairs));
  printf("nn-transormed AUC = %f\n", KD_train_auc(train_pairs, net));
  printf("auc_energy AUC = %f\n", auc_energy(data));
  
  sa_algorithm(params, &SA);

  printf("nn-transormed AUC = %f\n", KD_train_auc(train_pairs, net));
  mlp_save(net, netFile);

}

void KD_train_minimize_NN (const char *log, const char *dictFile, const char *netFile, double b)
{
  dictionary *dict = dictionary_load(dictFile);

  puts("Vytvareni treningovych dvojic");
  cell *train_pairs = KD_train_create_train_pairs3(dict, dict, 0);

  unsigned int inputs = matrix_size_m(element_get_matrix(cell_get(train_pairs, 0, 0)));
  mlp * net = mlp_load(netFile);

  gdm_sys *sys = gdm_alloc(mlp_get_total_connections(net));
  unsigned int i, j, k, pi;
  pi = 0;
  for (i = 0; i<mlp_get_num_layers(net)-1; i++) {
    for (j = 0; j<=mlp_get_neurons(net, i); j++) {
      for (k = 0; k<mlp_get_neurons(net, i+1); k++) {
        gdm_define_param(sys, pi, &(net->weights[i][j][k]));
        //sa_param_dmax_set(params, pi, 1/((double)mlp_get_neurons(net, i)));
        pi++;
      }
    }
  }

  double B = b;
  void **data = malloc(sizeof(void*)*3);
  data[0] = (void*)net;
  data[1] = (void*)train_pairs;
  data[2] = (void*)&B;

  sys->logFile = stdout;//fopen(log, "w");
  sys->lerning_rate = 10;
  sys->grad_type = right;
  sys->maxError = 0.001;
  sys->maxIter = 100;
  sys->data = data;
  sys->error = auc_energy;
  sys->param_prob = 0.001;
  sys->filename = log;

  gdm_optim(sys);
  
  printf("template-based AUC = %f\n", KD_train_auc2(train_pairs));
  //printf("nn-transormed AUC = %f\n", KD_train_auc(train_pairs, net));
  
  printf("nn-transormed AUC = %f\n", KD_train_auc(train_pairs, net));
  //mlp_save(net, netFile);

}

void compute_AUC (char *dictFile, char *netFile, char *log, unsigned int tests, char *avsFile)
{
  dictionary *dict = dictionary_load(dictFile);
  mlp *net = mlp_load(netFile);
  dictionary *avsDict;
  matrix **AS;
  cell *pairs;
  unsigned int i;
  double posteriori, teplateBased, value;
  
  if (avsFile !=NULL ) {
    avsDict = dictionary_load(avsFile);
    AS = dictionary_stat_norm(avsDict);
    dictionary_stat_norm_aply(dict, AS[0], AS[1]);
    matrix_free(AS[0]);
    matrix_free(AS[1]);
    free(AS);
    dictionary_free(avsDict);
  } else {
    AS = dictionary_stat_norm(dict);
    dictionary_stat_norm_aply(dict, AS[0], AS[1]);
    matrix_free(AS[0]);
    matrix_free(AS[1]);
    free(AS);
  }

  posteriori = teplateBased = 0;
  for (i = 1; i<=tests; i++) {
    pairs = KD_train_create_train_pairs3(dict, dict, 0);
    value = KD_train_auc2(pairs);
    teplateBased += value;
    printf("%i.\ntemplate-based AUC = %f\n", i, value);
    value = KD_train_auc(pairs, net);
    posteriori += value;
    printf("a posteriori AUC = %f\n", value);
    cell_free(pairs);
  }
  
  printf("AVARGE: template-based AUC = %f\n", teplateBased/((double)tests));
  printf("AVARGE: a posteriori AUC = %f\n", posteriori/((double)tests));
    
  FILE *pf;
  if (log!=NULL) {
    pf = fopen(log, "a");
    fprintf(pf, "\nAVARGE: template-based AUC = %f\nAVARGE: a posteriori AUC = %f\n", teplateBased/((double)tests), posteriori/((double)tests));
    fclose(pf);
  }

  mlp_free(net);
  dictionary_free(dict);
}

void stats_of_detector (void)
{

  dictionary *DICT = dictionary_load("/DISK2/Dokumenty/Diplomka_zpracovani_reci/c/SKI_project/data/KEYWORD_DICTIONARY_LPC");
  mlp *net = mlp_load("/DISK2/Dokumenty/Diplomka_zpracovani_reci/c/vysledky/70n_8n/sit_SA_0125_LPCC_70n_8n.txt");
  //matrix **avs = dictionary_stat_norm(DICT);
  //matrix_print(avs[0]);matrix_print(avs[1]);
  //dictionary_stat_norm_aply(DICT, avs[0], avs[1]);
  dictionary_transform_mlp (DICT, net);
  unsigned int n;
  sentence **sens = sentences_load("/DISK2/Dokumenty/Diplomka_zpracovani_reci/c/SKI_project/data/DETECTOR_TEST", &n, 10);

  //printf("%i x %i\n", sentence->m, sentence->n);

  //matrix_print_info(sentence);

  detector_list_opt opt;
  opt.min_frames = 2;
  opt.max_frames = 32;

  sentence_comp_features2(sens, n, lpcc_features1);
  sentence_mlp_transform2(sens, n, net);

  //matrix_print(matrix_transpose(KD_train_transform(sentence, net)));

  matrix *weight = dictionary_weight_design(DICT, &opt);
  matrix_print(weight);

  puts("Navrh tresholdu");
  //detector_treshold_try();

  //matrix **stats = detector_treshold_stats2(sens, n, DICT, &opt, 1, 4, net, weight);
  //matrix *treshold = detector_design_treshold(stats, n);

  //matrix_print(treshold);

}


void creating_dictionary (sentence **sens, unsigned int n, const char *dictFile, matrix *(trFunc)(matrix *, unsigned int, unsigned int, unsigned int))
{ 
  sentence_comp_features2(sens, n, trFunc);
  dictionary *dict = dictionary_create(sens, n);
  dictionary_save(dictFile, dict);
}


void test_AUC(const char *dictFile, const char *netFile)
{
  dictionary *dict = dictionary_load(dictFile);
  mlp *net = mlp_load(netFile);
  cell *pairs = KD_train_create_train_pairs3(dict, dict, 0);

  matrix **avs = dictionary_stat_norm(dict);
  dictionary_stat_norm_aply(dict, avs[0], avs[1]);

  printf("\ntemplate-based matching AUC = %f\n", KD_train_auc(pairs, net));
  printf("MLP-transformed matching AUC = %f\n", KD_train_auc2(pairs));

  mlp_free(net);
  dictionary_free(dict);
  cell_free(pairs);
}


int main (int argc, char *argv[])
{
  srand(time(NULL));
  /*
  matrix *A = matrix_alloc(1, 5);
  matrix_set(A, 0, 0, 1);
  matrix_set(A, 0, 1, 0);
  matrix_set(A, 0, 2, 1);
  matrix_set(A, 0, 3, 1);
  matrix_set(A, 0, 4, 0);
  matrix *B = matrix_alloc(1, 5);
  matrix_set(B, 0, 0, 0);
  matrix_set(B, 0, 1, 0);
  matrix_set(B, 0, 2, 0);
  matrix_set(B, 0, 3, 0);
  matrix_set(B, 0, 4, 0);
  matrix *C = matrix_alloc(1, 5);
  matrix_set(C, 0, 0, 0);
  matrix_set(C, 0, 1, 1);
  matrix_set(C, 0, 2, 1);
  matrix_set(C, 0, 3, 0);
  matrix_set(C, 0, 4, 0);
  matrix *D = matrix_alloc(1, 5);
  matrix_set(D, 0, 0, 0);
  matrix_set(D, 0, 1, 1);
  matrix_set(D, 0, 2, 0);
  matrix_set(D, 0, 3, 1);
  matrix_set(D, 0, 4, 0);
  matrix *E = matrix_alloc(1, 5);
  matrix_set(E, 0, 0, 0);
  matrix_set(E, 0, 1, 1);
  matrix_set(E, 0, 2, 1);
  matrix_set(E, 0, 3, 1);
  matrix_set(E, 0, 4, 0);

  puts("Word process");
  matrix_print(A);
  puts("detection");
  puts("B");
  matrix_print(B);
  puts("C");
  matrix_print(C);
  puts("D");
  matrix_print(D);
  puts("E");
  matrix_print(E);

  puts("Res");
  puts("B");
  matrix_print(detector_WER_ISD(A, B));
  puts("C");
  matrix_print(detector_WER_ISD(A, C));
  puts("D");
  matrix_print(detector_WER_ISD(A, D));
  puts("E");
  matrix_print(detector_WER_ISD(A, E));

matrix_print(detector_WER_Deletions(A, E));
 */
  
/*
  unsigned int i, N;
  sentence **sens;
  sens = sentences_load("/DISK2/Dokumenty/Diplomka_zpracovani_reci/c/SKI_project/data/KEYWORD_SENTENCES", &N);
  sentence_comp_features2(sens, N, lpcc_features1);
  sentences_save("/DISK2/Dokumenty/Diplomka_zpracovani_reci/c/SKI_project/data/KEYWORD_SENTENCES_LPCC", sens, N);
  sentence_comp_features2(sens, N, lpcc_features1);
  sentences_save("/DISK2/Dokumenty/Diplomka_zpracovani_reci/c/SKI_project/data/KEYWORD_SENTENCES_MFCC", sens, N);
  sentence_comp_features2(sens, N, lpcc_features1);
  sentences_save("/DISK2/Dokumenty/Diplomka_zpracovani_reci/c/SKI_project/data/KEYWORD_SENTENCES_MFCC_E_DELTA", sens, N);
  sentence_comp_features2(sens, N, lpcc_features1);
  sentences_save("/DISK2/Dokumenty/Diplomka_zpracovani_reci/c/SKI_project/data/KEYWORD_SENTENCES_LPCC_E_DELTA", sens, N);
  
  // Vytvoreni slovniku
  creating_dictionary(
  sens, N,
  "/DISK2/Dokumenty/Diplomka_zpracovani_reci/c/SKI_project/data/KEYWORD_DICTIONARY_LPC",
  mfcc_features1
  );
  creating_dictionary(
  sens, N,
  "/DISK2/Dokumenty/Diplomka_zpracovani_reci/c/SKI_project/data/KEYWORD_DICTIONARY_MFCC_E_DELTA",
  mfcc_features2
  );
  creating_dictionary(
  sens, N,
  "/DISK2/Dokumenty/Diplomka_zpracovani_reci/c/SKI_project/data/KEYWORD_DICTIONARY_LPCC",
  lpcc_features1
  );
  creating_dictionary(
  sens, N,
  "/DISK2/Dokumenty/Diplomka_zpracovani_reci/c/SKI_project/data/KEYWORD_DICTIONARY_LPCC_E_DELTA",
  lpcc_features2
  );
  //*/

  //compute_AUC(
  //    "/DISK2/Dokumenty/Diplomka_zpracovani_reci/c/SKI_project/data/TEST_DICT_MFCC",//_E_DELTA
  //    "/DISK2/Dokumenty/Diplomka_zpracovani_reci/c/vysledky/70n_8n_ZTR/sit_SA_100_ZTR_MFCC_70n_8n.txt",
  //    "/DISK2/Dokumenty/Diplomka_zpracovani_reci/c/vysledky/70n_8n_ZTR/log_SA_100_ZTR_MFCC_70n_8n.txt",
  //    10,
  //    "/DISK2/Dokumenty/Diplomka_zpracovani_reci/c/SKI_project/data/DICT_MFCC"
  //);
  
//*/
  
  //train_NN_SA(
  //  "/DISK2/Dokumenty/Diplomka_zpracovani_reci/c/log",
  //  "/DISK2/Dokumenty/Diplomka_zpracovani_reci/c/SKI_project/data/DICTIONARY",
  //  "/DISK2/Dokumenty/Diplomka_zpracovani_reci/c/sit.txt",
  //   0.25
  //);



  //test_AUC("/DISK2/Dokumenty/Diplomka_zpracovani_reci/c/SKI_project/data/DICT_LPCC", "/DISK2/Dokumenty/Diplomka_zpracovani_reci/c/sit.txt");

  //KD_train_minimize_NN(
  //  "/DISK2/Dokumenty/Diplomka_zpracovani_reci/c/log",
  //  "/DISK2/Dokumenty/Diplomka_zpracovani_reci/c/SKI_project/data/TEST_DICT_LPCC",
  //  "/DISK2/Dokumenty/Diplomka_zpracovani_reci/c/sit_SA_050_LPCC_70n_8n.txt",
  //   0.5
  //);

/*
  list *L = list_alloc();
  list_add_last(L, element_alloc_string("present"));
  list_add_last(L, element_alloc_string("people"));
  list_add_last(L, element_alloc_string("extra"));
  list_add_last(L, element_alloc_string("large"));
  list_add_last(L, element_alloc_string("hand"));
  list_add_last(L, element_alloc_string("government"));
  list_add_last(L, element_alloc_string("longer"));
  list_add_last(L, element_alloc_string("maybe"));
  list_add_last(L, element_alloc_string("however"));
  list_add_last(L, element_alloc_string("time"));
  list_add_last(L, element_alloc_string("black"));
  list_add_last(L, element_alloc_string("home"));
  list_add_last(L, element_alloc_string("garden"));
  dictionary *dict1 = dictionary_load("/DISK2/Dokumenty/Diplomka_zpracovani_reci/c/SKI_project/data/KEYWORD_DICTIONARY_LPCC");
  dictionary *dict2 = dictionary_filter_words(dict1, L);
  dictionary_print(dict2);
  dictionary_save("/DISK2/Dokumenty/Diplomka_zpracovani_reci/c/SKI_project/data/MY_KEYWORD_DICTIONARY_LPCC", dict2);
*/

  //const char train[200];
  //const char word[200];
  //const char train[200];
  //const char test[200];
  //const char net1[200];
  //const char net2[200];

  test_AUC("/DISK2/Dokumenty/Diplomka_zpracovani_reci/c/SKI_project/data/DICT_LPCC", "/DISK2/Dokumenty/Diplomka_zpracovani_reci/c/vysledky/sit_SA_050_ZTR_LPCC_70n_8n.txtGDM");


  const char *word="people";
  const char *train="/DISK2/Dokumenty/Diplomka_zpracovani_reci/c/SKI_project/data/slova/train__people.txt";
  const char *test="/DISK2/Dokumenty/Diplomka_zpracovani_reci/c/SKI_project/data/slova/test__people.txt";

  const char *net1="/DISK2/Dokumenty/Diplomka_zpracovani_reci/c/vysledky/70n_8n/sit_SA_025_LPCC_70n_8n.txt";
  const char *net2="/DISK2/Dokumenty/Diplomka_zpracovani_reci/c/vysledky/70n_8n/sit_SA_025_MFCC_70n_8n.txt";

  determine_treshold(
      test,
      lpcc_features1,
      train,word,
      net1
  );

  determine_treshold(
      test,
      mfcc_features1,
      train,word,
      net2
  );

  determine_treshold(
      test,
      lpcc_features1,
      train,word,
      NULL
  );

  determine_treshold(
      test,
      mfcc_features1,
      train,word,
      NULL
  );

  return EXIT_SUCCESS;

}