#include "dictionary.h"

/* * * * * * * * * * *
 * DICTIONARY CLASS  *
 * * * * * * * * * * */

dictionary_class *dictionary_class_alloc(unsigned int n)
{
  dictionary_class *dcl = malloc(sizeof(dictionary_class)); // Alokuje slovni tridu
  dcl->features = malloc(sizeof(matrix)*n); // alkouje pole matic (priznaku slov - promluvy)
  dcl->mean_length = -1;
  dcl->std_length = -1;
  dcl->phonemes = NULL;
  unsigned int i;
  for (i = 0; i<n; i++) {
    dcl->features[i] = NULL; // nastavi vsechny priznaky na 0
  }
  dcl->n = n; // nastavi pocet promluv ve slovni tride
  dcl->name = NULL; // inicializuje jmeno
}

void dictionary_class_free(dictionary_class *dict)
{
  unsigned int i;
  for (i = 0; i<dict->n; i++) {
    if (dict->features[i]!=NULL) { // Pokud je dana promluva nastavena (neni NULLova).
      matrix_free(dict->features[i]);
    }
  }
  free(dict->name);
  cell_free(dict->phonemes);
  free(dict);
}

dictionary_class *dictionary_class_clone(dictionary_class *dcl)
{
  int i;
  size_t L;
  char *name, *newName;

  // Alokace klonovanovane slovni tridy
  dictionary_class *newDcl = dictionary_class_alloc(dcl->n);

  // Klonovani jmena
  name = dictionary_class_get_name(dcl);
  L = strlen(name);
  newName = (char *) malloc(sizeof(char)*(L+1));
  memccpy(newName, name, 1, L+1);
  dictionary_class_set_name(newDcl, newName);

  // Klonovani fonemove transkripce
  dictionary_class_set_phonemes(newDcl, cell_clone(dictionary_class_get_phonemes(dcl)));

  // Klonovani priznaku
  for (i = 0; i<dcl->n; i++) {
    dictionary_class_set_features(newDcl, i, matrix_clone(dictionary_class_get_features(dcl, i)));
  }

  return newDcl;
}

void dictionary_class_set_name(dictionary_class *dcl, char *name)
{
  if (dcl->name!=NULL) {
    free(dcl->name);
  }
  unsigned int N;
  N = (strlen(name)+1);  // delka retezce NAME.
  dcl->name = malloc(sizeof(char)*N);  // Alokace pameti pro nazev slova (NAME).
  memcpy(dcl->name, (void*) name, N);  // Ulozeni nazvu slova.
}

void dictionary_class_set_features(dictionary_class *dcl, unsigned int i, matrix *features)
{
  #ifdef DICTIONARY_DEBUG
  if (dcl->n > i) {
  #endif
    dcl->features[i] = features;
    dcl->mean_length = -1;
    dcl->std_length = -1;
  #ifdef DICTIONARY_DEBUG
  } else {
    printf("%s:%i: error: Index mimo hranice pole matic. Index (%i) musi byt mensi nez %i.\n", __FILE__, __LINE__, i, dcl->n);
    exit(1);
  }
  #endif
}

void dictionary_class_set_phonemes(dictionary_class *dcl, cell *phonemes)
{
  dcl->phonemes = phonemes;
}

char *dictionary_class_get_name(dictionary_class *dcl)
{
  return dcl->name;
}

matrix *dictionary_class_get_features(dictionary_class *dcl, unsigned int i)
{
  #ifdef DICTIONARY_DEBUG
  if (dcl->n > i) {
  #endif
    return dcl->features[i];
  #ifdef DICTIONARY_DEBUG
  } else {
    printf("%s:%i: error: Index mimo hranice pole matic. Index (%i) musi byt mensi nez %i.\n", __FILE__, __LINE__, i, dcl->n);
    exit(1);
  }
  #endif
  return NULL;
}

cell *dictionary_class_get_phonemes(dictionary_class *dcl)
{
  return dcl->phonemes;
}

double dictionary_class_mean_length(dictionary_class *dcl)
{
  if (dcl->mean_length != -1) { // Pokud je vypocitany prumer.
    return dcl->mean_length;
  } else { // Pokud neni vypocitany prumer.
    double Length = 0;
    matrix *f;
    unsigned int i;
    // Soucet vsech delek promluv.
    for (i = 0; i < dcl->n; i++) {
      f = dictionary_class_get_features(dcl, i);
      if (f!=NULL) { // Pokud je dana promluva nastavena.
        Length += ((double) matrix_size_n(f));
        //printf("%f\n", Length);
      } else {
        printf("%s:%i: error: Slovni trida neni kompletni.\n", __FILE__, __LINE__);
        exit(1);
      }
    }
    // Vypocet prumeru.
    dcl->mean_length = Length/((double)dcl->n);
    //printf("E = %f\n", Length/((double)dcl->n));
  }
  // Vrati prumenou delku slovni tridy.
  return dcl->mean_length;
}

double dictionary_class_std_length(dictionary_class *dcl)
{
  if (dcl->std_length != -1) { // Pokud je smerodatna odchylka vypocitana.
    return dcl->std_length;
  } else { // Pokud neni vypocitana.
    matrix *f;
    double meanLength = dictionary_class_mean_length(dcl);//printf("mean = %f\n", meanLength);
    double Lpow = 0;
    unsigned int i;
    for (i = 0; i < dcl->n; i++) {
      f = dictionary_class_get_features(dcl, i);
      if (f!=NULL) { // Pokud je dana promluva nastavena.
        Lpow += pow(((double) matrix_size_n(f))-meanLength, 2);//printf("std = %f\n", Lpow);
      } else {
        printf("%s:%i: error: Slovni trida neni kompletni.\n", __FILE__, __LINE__);
      }
    }
    // Vypocet smerodatne odchylky.
    dcl->std_length = sqrt(Lpow/((double)dcl->n));
  }
  // Vrati smerodatnou odchlyku delek promluv ve slovni tride DCL.
  return dcl->std_length;
}


void dictionary_transform_mlp (dictionary *dict, mlp *net)
{
  register unsigned int i, j, m, n;
  unsigned int mi, mo, nmat;
  dictionary_class *dcl;
  matrix *mat, *trmat;
  mi = mlp_get_num_inputs(net);
  mo = mlp_get_num_outputs(net);//printf("%i %i\n", mi, mo);
  double x[mi], y[mo];

  for (i = 0; i<dict->n; i++) { // Pro vsechny slova ve slovniku dict
    dcl = dictionary_get(dict, i); // slovni trida ze slovniku
    // Transformace vsech priznaku ve slovni tride
    for (j = 0; j<dcl->n; j++) {
      mat = dictionary_class_get_features(dcl, j); // Priznaky
      //printf("%i %i\n", mat->m, mat->n);
      nmat = matrix_size_n(mat);
      trmat = matrix_alloc(mo, nmat);

      // Transformace
      for (n = 0; n<nmat; n++) {
        for (m = 0; m<mi; m++) {
          x[m] = matrix_get(mat, m, n);
        }
        mlp_run(net, x, y);
        for (m = 0; m<mo; m++) {
          matrix_set(trmat, m, n, y[m]);
        }
      }

      // Vymazani puvodnich priznaku a prirazeni transformovanych priznaku
      matrix_free(mat);
      dictionary_class_set_features(dcl, j, trmat);

    } // transformace slovni tridy

  } // transformace slovniku
  
}

/* * * * * * * *
 * DICTIONARY  *
 * * * * * * * */

dictionary *dictionary_alloc(unsigned int n)
{
  dictionary *dict = malloc(sizeof(dictionary));
  dict->classes = malloc(sizeof(dictionary_class *)*n);
  dict->n = n;
  return dict;
}

void dictionary_free(dictionary *dict)
{
  free(dict->classes);
  free(dict);
}

int dictionary_set(dictionary *dict, unsigned int i, dictionary_class *dcl)
{
  #ifdef DICTIONARY_DEBUG
  if (dict->n > i) {
  #endif
    dict->classes[i] = dcl;
    return 1;
  #ifdef DICTIONARY_DEBUG
  } else {
    printf("%s:%i: error: Index (%i) mimo hranice slovniku (%i).\n", __FILE__, __LINE__, i, dict->n);
  }
  #endif
  return 0;
}

dictionary_class *dictionary_get(dictionary *dict, unsigned int i)
{
  #ifdef DICTIONARY_DEBUG
  if (dict->n > i) {
  #endif
    return dict->classes[i];
  #ifdef DICTIONARY_DEBUG
  } else {
    printf("%s:%i: error: Index (%i) mimo hranice slovniku (%i).\n", __FILE__, __LINE__, i, dict->n);
  }
  #endif
  return NULL;
}

cell *dictionary_get_features(dictionary *dict, unsigned int i)
{
  dictionary_class *dcl = dictionary_get(dict, i);
  cell *out = cell_alloc(dcl->n, 1);
  unsigned int k;
  for (k = 0; k<dcl->n; k++) {
    cell_set(out, k, 0, element_alloc_matrix(matrix_clone(dictionary_class_get_features(dcl, k))));
  }
  return out;
}

char *dictionary_get_name(dictionary *dict, unsigned int i)
{
  dictionary_class *dcl = dictionary_get(dict, i);
  return dictionary_class_get_name(dcl);
}

cell *dictionary_get_phonemes(dictionary *dict, unsigned int i)
{
  dictionary_class *dcl = dictionary_get(dict, i);
  return dictionary_class_get_phonemes(dcl);
}

double dictionary_get_mean(dictionary *dict, unsigned int i)
{
  dictionary_class *dcl = dictionary_get(dict, i);
  return dictionary_class_mean_length(dcl);
}

dictionary *dictionary_filter_words(dictionary *dict, list *words)
{
  unsigned int i, N;
  dictionary_class *dcl;
  dictionary *filtredDict;
  char *pch;

  // Spocitani poctu vyskytu pozadovanych slov ve slovniku DICT
  list_first(words);
  N = 0;
  do {
    pch = element_get_string(list_get_curent(words));
    for (i = 0; i<dict->n; i++) {
      if (strcmp(pch, dictionary_get_name(dict, i)) == 0) {
        N++;
        break;
      }
    }
  } while (list_next(words));

  // Pokud neobsahuje zade slovo.
  if (N == 0) {
    return NULL;
  }

  // Alokace filtrovaneho slovniku
  filtredDict = dictionary_alloc(N);
  N = 0;

  // Ulozeni slovnich trid do filtrovaneho slovniku
  list_first(words);
  do {
    pch = element_get_string(list_get_curent(words));
    for (i = 0; i<dict->n; i++) {
      if (strcmp(pch, dictionary_get_name(dict, i)) == 0) {
        dcl = dictionary_get(dict, i);
        dictionary_set(filtredDict, N, dictionary_class_clone(dcl));
        N++;
        break;
      }
    }
  } while (list_next(words));

  return filtredDict;
}

dictionary *dictionary_cat(dictionary **dict_array, unsigned int n)
{
  unsigned int i, j, k, N, found;
  dictionary *dict;
  dictionary_class *dcl;
  list *words, *features;
  cell *word_class, *phonemes;
  char *name, *name2;

  words = list_alloc();

  // Zjistim vyskyt vsech slov.
  for (i = 0; i<n; i++) {
    //printf("%i/%i\n", i+1, n);
    dict = dict_array[i];
    for (j = 0; j<dict->n; j++) {
      dcl = dictionary_get(dict, j);
      name = dictionary_class_get_name(dcl);

      if (list_get_size(words)==0) { // Pokud je seznam prazdny

        word_class = cell_alloc(3, 1);
        features = list_alloc();
        cell_set(word_class, 0, 0, element_alloc_string(name)); // nazev slova
        cell_set(word_class, 1, 0, element_alloc_list(features)); // seznam priznaku
        cell_set(word_class, 2, 0, element_alloc_cell(dictionary_class_get_phonemes(dcl))); // foneticka transkripce

        // Prida do seznamu priznaku daneho slova priznaky z tridy DCL.
        for (k = 0; k<dcl->n; k++) {
          list_add_last(features, element_alloc_matrix(dictionary_class_get_features(dcl, k))); // Ulozi danou matici
        }

        list_add_last(words, element_alloc_cell(word_class));

      } else { // Pokud sezname NENI prazdny

        found = 0;
        list_first(words);
        do {

          word_class = element_get_cell(list_get_curent(words));
          name2 = element_get_string(cell_get(word_class , 0, 0));
          if (strcmp(name, name2)==0) { // Pokud dane slovo je uz v seznamu WORDS
            features = element_get_list(cell_get(element_get_cell(list_get_curent(words)) , 1, 0));

            // Prida do seznamu priznaku daneho slova priznaky z tridy DCL.
            for (k = 0; k<dcl->n; k++) {
              list_add_last(features, element_alloc_matrix(dictionary_class_get_features(dcl, k))); // Ulozi danou matici
            }
            
            found = 1; // mnalezeno
            break; // vic stejnych slov v tomto sloniku uz neni
          }

        } while(list_next(words));

        if (found==0) { // Slovo jeste v seznamu WORDS neni.

          word_class = cell_alloc(3, 1);
          features = list_alloc();
          cell_set(word_class, 0, 0, element_alloc_string(name)); // nazev slova
          cell_set(word_class, 1, 0, element_alloc_list(features)); // seznam priznaku
          cell_set(word_class, 2, 0, element_alloc_cell(dictionary_class_get_phonemes(dcl))); // foneticka transkripce

          // Prida do seznamu priznaku daneho slova priznaky z tridy DCL.
          for (k = 0; k<dcl->n; k++) {
            list_add_last(features, element_alloc_matrix(dictionary_class_get_features(dcl, k))); // Ulozi danou matici
          }
          
          list_add_last(words, element_alloc_cell(word_class));
          
        }

      }

    }
  }

  //list_print_info(words);
  dictionary *OUT = dictionary_alloc(list_get_size(words));
  size_t length;
  list_first(words);
  for (i = 0; i<list_get_size(words); i++) {

    //printf("%i/%i\n", i+1, list_get_size(words));
    // Vytvoreni slovni tridy
    word_class = element_get_cell(list_get_curent(words));
    features = element_get_list(cell_get(word_class, 1, 0));

    // Ulozeni priznaku slovni tridy
    dcl = dictionary_class_alloc(list_get_size(features));
    list_first(features);
    for (j = 0; j<dcl->n; j++) {
      dictionary_class_set_features(dcl, j, element_get_matrix(list_get_curent(features)));
      list_next(features);
    }

    // Nastaveni jmena slovni tridy
    name = element_get_string(cell_get(word_class, 0, 0));
    length = strlen(name);
    name2 = malloc(sizeof(char)*(length+1));
    memcpy(name2, name, length+1);
    dictionary_class_set_name(dcl, name2);

    // Nastaveni foneticke transrkipce
    phonemes = element_get_cell(cell_get(word_class, 2, 0));
    dictionary_class_set_phonemes(dcl, cell_clone(phonemes));

    // Ulozi slovni tridu do vystupniho slovniku OUT.
    dictionary_set(OUT, i, dcl);

    list_next(words);
  }

  // Znicim nepotrebna data i s puvodnimi slovniky.
  list_throw_away(words);
  for (i = 0; i<n; i++) {
    dictionary_free(dict_array[i]);
  }

  //dictionary_print(OUT);

  return OUT;

}

void dictionary_save(char *filename, dictionary *dict)
{
  unsigned long length = 0;
  unsigned int i, j;
  dictionary_class *dcl;
  matrix *mat;
  // Zjisteni delky retezce pro ulozeni slovniku
  for (i = 0; i<dict->n; i++) {
    dcl = dictionary_get(dict, i);
    length += strlen(dictionary_class_get_name(dcl))+14;//printf("length = %li\n", length);
    for (j = 0; j<dcl->n; j++) {
      mat = dictionary_class_get_features(dcl, j);
      length += matrix_save_length(mat)+1;//printf("length = %li\n", length);
    }
    for (j = 0; j<cell_size_n(dcl->phonemes); j++) {
      length += strlen(element_get_string(cell_get(dcl->phonemes, 0, j)))+1;//printf("length = %li\n", length);
    }
  }
  
  char *str = malloc(sizeof(char)*length);
  
  for (i = 0; i<length; i++) {
    str[i] = 0;
  }
  
  char *buff;
  char buffer[100];
  // Vytvareni retezce
  for (i = 0; i<dict->n; i++) {

    dcl = dictionary_get(dict, i);
    buff = dictionary_class_get_name(dcl);//printf("%s\n", buff);
    //printf("%i/%i %s\n", i+1, dict->n, buff);
    strcat(str, buff);//printf("%s\n", str);
    sprintf(buffer, " %i\n", dcl->n);
    strcat(str, buffer);//printf("%s\n", str);
    
    sprintf(buffer, "%i %s", cell_size_n(dcl->phonemes), element_get_string(cell_get(dcl->phonemes, 0, 0)));
    strcat(str, buffer);
    //printf("%i / %i  ,  %i\n", i+1, dict->n, cell_size_n(dcl->phonemes));
    for (j = 1; j<cell_size_n(dcl->phonemes); j++) {
      sprintf(buffer, " %s", element_get_string(cell_get(dcl->phonemes, 0, j)));
      strcat(str, buffer);
    }
    strcat(str, "\n");
    
    for (j = 0; j<dcl->n; j++) {
      mat = dictionary_class_get_features(dcl, j);
      buff = matrix_save_save_string(mat);//printf("%s", buff);
      strcat(str, buff);
      free(buff);
      strcat(str, "\n");
      //printf("%s", str);
    }
  }
  
  // Ukladani do souboru
  FILE *pf = fopen(filename, "w");printf("%s\n", str);
  fputs(str, pf);
  //printf("%s", str);

  fclose(pf);
  free(str);
}

cell *dictionary_phoneme_parse(char *str)
{
  char buffer[20], *pch, *newstr;
  list *L = list_alloc();
  cell *out;
  unsigned int N, n, i;
  sscanf(str, "%i", &N);//printf("pn = %i\n", N);

  for (i = 0; i<N; i++) {
    
    while (str[0]!=(" ")[0]) {
      //if (str[0]!=("\n")[0]){
        //break;
      //}
      str++;
    }
    str++;
    //if (str[0]!=("\n")[0]){break;};

    //printf("%.10s\n", str);

    sscanf(str, "%s", buffer);
    //printf("%s\n", buffer);
    
    n = strlen(buffer)+1;
    newstr = malloc(sizeof(char)*n);
    memcpy(newstr, buffer, n);
    newstr[n-1] = NULL;//printf("%s\n", newstr);

    list_add_last(L, element_alloc_string(newstr));//list_print(L);

  }

  n = list_get_size(L);
  list_first(L);
  out = cell_alloc(1, n);
  for (i = 0; i<n; i++) {
    cell_set(out, 0, i, element_clone(list_get_curent(L)));
    list_next(L);
  }

  list_free(L);
  
  return out;

}

dictionary *dictionary_load(char *filename)
{
  printf("Nacitam slovnik \"%s\"\n", filename);
  FILE *pFile = fopen(filename, "r");
  if (pFile!=NULL) {

    // Zjisteni velikosti souboru :
    fseek (pFile , 0 , SEEK_END);
    long int lSize = ftell(pFile);
    rewind (pFile);

    char *str = (char*) malloc(sizeof(char)*lSize);
    size_t result = fread(str, 1, lSize, pFile);

    if (result!=lSize) {
      printf("%s:%i: error: Nepodarilo se nacist data ze souboru.\n", __FILE__, __LINE__);
      exit (2);
    }

    char *pch, *name, buffer[100], *pnum;
    unsigned int *n, length;
    n = malloc(sizeof(unsigned int));
    dictionary_class *dcl;
    matrix *mat;
    list *names, *features, *phlist;
    cell *phonemes;
    names = list_alloc();
    features = list_alloc();
    phlist = list_alloc();
    cell *class_features;
    pch = strtok(str, "\n");
    unsigned int i, j;

    while (pch!=NULL) {

      sscanf(pch, "%s %i", buffer, n);
      class_features = cell_alloc(*n, 1);
      length = strlen(buffer);
      
      name = malloc(sizeof(char)*(length+1));
      for(j = 0; j<length+1; j++) {
        name[j] = buffer[j];
      }

      list_add_last(names, element_alloc_string(name));
      list_add_last(features, element_alloc_cell(class_features));

      pch = strtok(NULL, "\n");
      phonemes = dictionary_phoneme_parse(pch);
      list_add_last(phlist, element_alloc_cell(phonemes));

      for (j = 0; j<*n; j++) {
        pch = strtok(NULL, "\n");
        //printf("%s\n", pch);
        mat = matrix_load_string(pch);
        cell_set(class_features, j, 0, element_alloc_matrix(mat));
      }

      pch = strtok(NULL, "\n");

    }
    
    dictionary *dict = dictionary_alloc(list_get_size(names));
    list_first(names);
    list_first(features);
    list_first(phlist);
    char *copy;
    
    for (i = 0; i<dict->n; i++) {

      class_features = element_get_cell(list_get_curent(features));
      dcl = dictionary_class_alloc(cell_size_m(class_features));
      phonemes = cell_clone(element_get_cell(list_get_curent(phlist)));
      dictionary_class_set_phonemes(dcl, phonemes);
      for (j = 0; j<dcl->n; j++) {
        dictionary_class_set_features(dcl, j, matrix_clone(element_get_matrix(cell_get(class_features, j, 0))));
      }

      name = element_get_string(list_get_curent(names));
      length = strlen(name);
      copy = malloc(sizeof(char)*(length+1));
      memcpy(copy, name, (size_t) (length+1));

      dictionary_class_set_name(dcl, copy);
      dictionary_set(dict, i, dcl);
      list_next(names);
      list_next(features);
      list_next(phlist);
    }

    //list_print_info(names);
    list_free(names);
    list_free(features);
    list_free(phlist);
    free(n);
    return dict;

  } else {
    printf("%s:%i: error: Nepodarilo se nacist soubor\"%s\".\n", __FILE__, __LINE__, filename);
    exit (1);
  }
}


matrix *dictionary_weight_design(dictionary *dict, detector_list_opt *opt)
{
  unsigned int i, j;
  dictionary_class *dcl;
  double w, u, sigma;
  
  matrix *weight = matrix_alloc(dict->n, opt->max_frames - opt->min_frames + 1);

  // Pro vsechny slova ve vete se navrhne vahovaci matice k jednotlivym oknum.
  for (i = 0; i<matrix_size_m(weight); i++) {
    // slovni trida
    dcl = dictionary_get(dict, i);
    u = dictionary_class_mean_length(dcl);
    sigma = dictionary_class_std_length(dcl);
    
    // Navrh jednotlivych vah pro slovni tridu DCL.
    for (j = 0; j<matrix_size_n(weight); j++) {
      w = 1/(sigma*sqrt(2*PI))*exp(-pow(j+opt->min_frames-u, 2)/(2*pow(sigma, 2)));
      matrix_set(weight, i, j, w);
    }
    
  }

  return weight;
}

int dictionary_contain_word(dictionary *dict, char *word)
{
  unsigned int i, n;
  char *dict_word;

  // Zjisteni poctu vyskytu
  for (i = 0; i< dict->n; i++) {
    dict_word = dictionary_get_name(dict, i);
    if (strcmp(dict_word, word)==0) { // Pokud dane slovo ze slovniku DICT se shoduje s WORD
      return 1;
    }
  }

  return 0;
}

dictionary *dictionary_create(sentence **sens, unsigned int n)
{
  //printf("n = %i\n", n);
  list *words = list_alloc();
  list *features;
  cell *foo;
  char *pch;
  unsigned int i, j, k, l, found_in_list;
  size_t length;
  matrix *mat, *sen_features;

  for (i = 0; i<n; i++) { // Pro kazdou vetu

    // Overeni jestli jsou ve vete vypocitane priznaky.
    if (sens[i]->features==NULL) {
      printf("%s:%i: Ve vete s indexem %i nejsou vypocitane proznaky.\n", __FILE__, __LINE__, i);
      exit(1);
    }

    for (j = 0; j<sens[i]->nwords; j++) { // Pro kazde slovo ve vete
      
      if (list_get_size(words)!=0) { // Pokud uz v seznamu WORDS je nejake slovo

        // Najdu jestli je v seznamu words jiz dane slovo
        found_in_list = 0;
        list_first(words);
        do {

            // Pokud seznam slov obsahuje dane slovo
            pch = element_get_string(cell_get(element_get_cell(list_get_curent(words)), 0, 0));
            //printf("%s %s\n", pch, sens[i]->words[j]);
            if (strcmp(pch, sens[i]->words[j])==0) {
              
              foo = element_get_cell(list_get_curent(words));
              features = element_get_list(cell_get(foo, 1, 0));
              sen_features = sens[i]->features; // Priznaky vety
              //printf("%i %i\n", matrix_size_m(sen_features), matrix_size_n(sen_features));
              mat = matrix_alloc(matrix_size_m(sen_features) ,sens[i]->feature_end[j]-sens[i]->feature_beg[j]+1);//printf("%i %i\n", matrix_size_m(mat), matrix_size_n(mat));
              // Ulozeni priznaku slova.
              for (k = 0; k<matrix_size_m(mat); k++) {
                for (l = 0; l<matrix_size_n(mat); l++) {
                  matrix_set(mat, k, l, matrix_get(sen_features, k, l+sens[i]->feature_beg[j]));
                }
              }
              list_add_last(features, element_alloc_matrix(mat));
              found_in_list = 1;
              break; // V kazdem slovniku muze byt kazde slovo jen jednou.
            }

        } while ((list_next(words))&(found_in_list==0));

        if (found_in_list == 0) { // Pokud slovo v seznamu nebylo nalezeno.
          
          foo = cell_alloc(3, 1);
          list_add_last(words, element_alloc_cell(foo));
          // Zkopirovani retezce slova
          length = strlen(sens[i]->words[j]);
          pch = malloc(sizeof(char)*(length+1));
          memcpy(pch, sens[i]->words[j], length+1);//printf("%s %i\n", pch, length);
          cell_set(foo, 0, 0, element_alloc_string(pch));

          // Alokace priznaku pro dane slovo
          features = list_alloc();
          sen_features = sens[i]->features; // Priznaky vety
          mat = matrix_alloc(matrix_size_m(sen_features) ,sens[i]->feature_end[j]-sens[i]->feature_beg[j]+1);//printf("%i %i\n", sens[i]->feature_end[j], sens[i]->feature_beg[j]);
          // Ulozeni priznaku slova.
          for (k = 0; k<matrix_size_m(mat); k++) {
            for (l = 0; l<matrix_size_n(mat); l++) {
              matrix_set(mat, k, l, matrix_get(sen_features, k, l+sens[i]->feature_beg[j]));
            }
          }
          list_add_last(features, element_alloc_matrix(mat));
          cell_set(foo, 1, 0, element_alloc_list(features));

          // ulozeni fonenmu slova
          cell_set(foo, 2, 0, element_alloc_cell(cell_clone(sens[i]->phonemes[j])));

        }

      } else { // Pokud je WORDS prazdny

        foo = cell_alloc(3, 1);
        list_add_last(words, element_alloc_cell(foo));
        // Zkopirovani retezce slova
        length = strlen(sens[i]->words[j]);
        pch = malloc(sizeof(char)*(length+1));
        memcpy(pch, sens[i]->words[j], length+1);//printf("%s %i\n", pch, length);
        cell_set(foo, 0, 0, element_alloc_string(pch));

        // Alokace priznaku pro dane slovo
        features = list_alloc();
        sen_features = sens[i]->features; // Priznaky vety
        mat = matrix_alloc(matrix_size_m(sen_features) ,sens[i]->feature_end[j]-sens[i]->feature_beg[j]+1);
        // Ulozeni priznaku slova.
        for (k = 0; k<matrix_size_m(mat); k++) {
          for (l = 0; l<matrix_size_n(mat); l++) {
            matrix_set(mat, k, l, matrix_get(sen_features, k, l+sens[i]->feature_beg[j]));
          }
        }
        list_add_last(features, element_alloc_matrix(mat));
        cell_set(foo, 1, 0, element_alloc_list(features));

        // ulozeni fonenmu slova
        cell_set(foo, 2, 0, element_alloc_cell(cell_clone(sens[i]->phonemes[j])));

      }

    }

  }

  // Ulozeni slovniku
  dictionary_class *dcl;
  dictionary *dict = dictionary_alloc(list_get_size(words));
  list_first(words);
  i = 0;
  do {

    foo = element_get_cell(list_get_curent(words));
    features = element_get_list(cell_get(foo, 1, 0));//printf("%i\n", list_get_size(features));
    // Alokace slovni tridy
    dcl = dictionary_class_alloc(list_get_size(features));
    // Ulozeni slova
    dictionary_class_set_name(dcl, element_get_string(cell_get(foo, 0, 0)));
    // Ulozeni fonemu
    dictionary_class_set_phonemes(dcl, cell_clone(element_get_cell(cell_get(foo, 2, 0))));
    // Ulozeni priznaku
    list_first(features);
    j = 0;
    do {

      dictionary_class_set_features(dcl, j++, matrix_clone(element_get_matrix(list_get_curent(features))));

    } while (list_next(features));

    dictionary_set(dict, i++, dcl);

  } while (list_next(words));

  list_free(words);
  return dict;

}

matrix **dictionary_stat_norm(dictionary *dict)
{
  unsigned int i, j, k, l, m, n;
  double value;
  matrix *avarge, *stdev, **OUT;
  dictionary_class *dcl;
  matrix *features;

  // Predpoklada ze ve slovniku jsou vypocitane priznaky
  if (dict->classes[0]->features==NULL) {
    fprintf(stderr, "%s: %i: Nejsou vypocitane priznaky slovniku.\n", __FILE__, __LINE__);
  } else {
    m = matrix_size_m(dict->classes[0]->features[0]);
  }

  // Alokace vystupnich hodnot
  avarge = matrix_alloc(m, 1);
  stdev = matrix_alloc(m, 1);

  // Spocitani poctu priznaku
  n = 0;
  for (i = 0; i<dict->n; i++) {
    dcl = dictionary_get(dict, i);
    for (j = 0; j<dcl->n; j++) {
      n += matrix_size_n(dictionary_class_get_features(dcl, j));
    }
  }

  // Vypocet prumeru v jednotlivych radcich
  matrix_zeros(avarge);
  for (i = 0; i<dict->n; i++) {
    dcl = dictionary_get(dict, i);
    for (j = 0; j<dcl->n; j++) {
      features = dictionary_class_get_features(dcl, j);
      for (k = 0; k<matrix_size_n(features); k++) {
        for (l = 0; l<m; l++) {
          matrix_set(avarge, l, 0, matrix_get(avarge, l, 0)+matrix_get(features, l, k));
        }
      }
    }
  }
  for (i = 0; i<matrix_size_m(avarge); i++) {
    matrix_set(avarge, i, 0, matrix_get(avarge, i, 0) / n);
  }

  // Vypocet smerodatne odchylky
  matrix_zeros(stdev);
  for (i = 0; i<dict->n; i++) {
    dcl = dictionary_get(dict, i);
    for (j = 0; j<dcl->n; j++) {
      features = dictionary_class_get_features(dcl, j);
      for (k = 0; k<matrix_size_n(features); k++) {
        for (l = 0; l<m; l++) {
          matrix_set(stdev, l, 0, matrix_get(stdev, l, 0)+pow(matrix_get(features, l, k)-matrix_get(avarge, l, 0), 2));
        }
      }
    }
  }
  for (i = 0; i<matrix_size_m(avarge); i++) {
    matrix_set(stdev, i, 0, sqrt(matrix_get(stdev, i, 0) / n));
  }

  // Alokace vystupu
  OUT = (matrix **)malloc(sizeof(matrix*)*2);
  OUT[0] = avarge;
  OUT[1] = stdev;

  return OUT;

}

void dictionary_stat_norm_aply(dictionary *dict, matrix *avarge, matrix *stdev)
{

  unsigned int i, j, k, l, m;
  dictionary_class *dcl;
  matrix *features;
  m = matrix_size_m(avarge);

  // Aplikace stat_norm na proznaky
  for (i = 0; i<dict->n; i++) {
    dcl = dictionary_get(dict, i);
    for (j = 0; j<dcl->n; j++) {
      features = dictionary_class_get_features(dcl, j);
      for (k = 0; k<matrix_size_n(features); k++) {
        for (l = 0; l<m; l++) {
          matrix_set(features, l, k, (matrix_get(features, l, k)-matrix_get(avarge, l, 0))/matrix_get(stdev, l, 0));
        }
      }
    }
  }

}


matrix **dictionary_somp_basis(dictionary *dict, unsigned int T)
{
  unsigned int i, j, k, l, col_foo, col_features, N;
  dictionary_class *dcl;
  matrix *features, *foo, **out;
  N = 0;
  for (i = 0; i < dict->n; i++) {
    dcl = dictionary_get(dict, i);
    for (j = 0; j < dcl->n; j++) {
      foo = dictionary_class_get_features(dcl, j);
      N += matrix_size_n(foo);
    }
  }

  features = matrix_alloc(matrix_size_m(foo), N);

  col_features = 0;
  for (i = 0; i < dict->n; i++) {
    dcl = dictionary_get(dict, i);
    for (j = 0; j < dcl->n; j++) {
      foo = dictionary_class_get_features(dcl, j);
      for (l = 0; l<matrix_size_n(foo); l++) {
        for (k = 0; k < matrix_size_m(features); k++) {
          matrix_set(features, k, col_features, matrix_get(foo, k, l));
        }
        col_features++;
      }
    }
  }

  out = somp_algorithm(features, T);
  
  matrix_free(features);
  return out;

}

void dictionary_somp_projection(dictionary *dict, matrix *basis)
{
  unsigned int i, j, k;
  dictionary_class *dcl;
  matrix *features, *new_features;
  
  for (i = 0; i < dict->n; i++) {
    dcl = dictionary_get(dict, i);
    for (j = 0; j < dcl->n; j++) {
      features = dictionary_class_get_features(dcl, j);
      new_features = somp_base_projection(features, basis);
      matrix_free(features);
      dictionary_class_set_features(dcl, j, new_features);

    }
  }
}


void dictionary_print(dictionary *dict)
{
  unsigned int i, j;
  dictionary_class *dcl;
  for (i = 0; i<dict->n; i++) {
    dcl = dictionary_get(dict, i);
    printf("%ix %s (", dcl->n, dictionary_class_get_name(dcl));  
    for (j = 0; j<cell_size_n(dcl->phonemes); j++) {
      printf(" \"%s\"", element_get_string(cell_get(dcl->phonemes, 0, j)));
    }
    printf(") (");
    for (j = 0; j<dcl->n; j++) {
      printf(" %ix%i", matrix_size_m(dcl->features[j]), matrix_size_n(dcl->features[j]));
    }
    printf(" )\n");
  }
}


/* * * * * * *
 * SENTENCE  *
 * * * * * * */


sentence *sentence_alloc(unsigned int n)
{
  sentence *sen = malloc(sizeof(sentence));
  sen->signal = NULL;
  sen->features = NULL;
  sen->words = malloc(sizeof(char*)*n);
  sen->nwords = n;
  sen->phonemes = malloc(sizeof(cell*)*n);
  sen->begining = malloc(sizeof(unsigned int)*n);
  sen->ending = malloc(sizeof(unsigned int)*n);
  sen->feature_beg = malloc(sizeof(unsigned int)*n);
  sen->feature_end = malloc(sizeof(unsigned int)*n);
  sen->win = SENTENCE_BASE_WIN;
  sen->inc = SENTENCE_BASE_INC;
  unsigned int i;
  for (i = 0; i<n; i++) {
    sen->begining[i] = 0;
    sen->ending[i] = 0;
    sen->feature_beg[i] = 0;
    sen->feature_end[i] = 0;
    sen->words[i] = NULL;
    sen->phonemes[i] = NULL;
  }
  return sen;
}

void sentence_free(sentence *sen)
{
  unsigned int i;

  if (sen->features!=NULL) {
    matrix_free(sen->features);
  }

  if (sen->signal!=NULL) {
    matrix_free(sen->signal);
  }
  
  for (i = 0; i<sen->nwords; i++) {
    if (sen->words[i]!=NULL) {
      free(sen->words[i]);
    }
    if (sen->phonemes[i]!=NULL) {
      cell_free(sen->phonemes[i]);
    }
  }

  free(sen->begining);
  free(sen->ending);
  free(sen->feature_beg);
  free(sen->feature_end);
  free(sen);
}

sentence **sentences_create(cell *pc, unsigned int *n)
{
  unsigned int i, j;
  size_t L;
  *n = cell_size_n(pc);
  cell *foo;
  matrix *moo;
  char *word, *copy_word;
  sentence **out = malloc(sizeof(sentence*) * (*n));

  for (i = 0; i<(*n); i++) {
    foo = element_get_cell(cell_get(pc, 1, i));
    out[i] = sentence_alloc(cell_size_m(foo));
  }

  for (i = 0; i<cell_size_n(pc); i++) {
    // Ulozeni signalu
    moo = element_get_matrix(cell_get(pc, 0, i));
    out[i]->signal = matrix_clone(moo);

    // Ulozeni slov
    foo = element_get_cell(cell_get(pc, 1, i));
    for (j = 0; j<cell_size_m(foo); j++) {
      // Zkopirovani daneho slova
      word = element_get_string(cell_get(foo, j, 0));
      L = strlen(word);
      copy_word = malloc(sizeof(char)*(L+1));
      memcpy(copy_word, word, L);
      copy_word[L] = 0;
      // Ulozeni do pole slov ve vete
      out[i]->words[j] = copy_word;
    }

    // Ulozeni fonemu
   foo = element_get_cell(cell_get(pc, 2, i));
   for (j = 0; j<cell_size_m(foo); j++) {
     out[i]->phonemes[j] = cell_clone(element_get_cell(cell_get(foo, j, 0)));
    }
    
    // Ulozeni zacatku slov
    moo = element_get_matrix(cell_get(pc, 3, i));
    for (j = 0; j<matrix_size_m(moo); j++) {
      out[i]->begining[j] = (unsigned int) matrix_get(moo, j, 0);
    }
    
    // Ulozeni koncu slov
    moo = element_get_matrix(cell_get(pc, 4, i));
    for (j = 0; j<matrix_size_m(moo); j++) {
      out[i]->ending[j] = (unsigned int) matrix_get(moo, j, 0);
    }

    // Ulozeni frekvence signalu slova.
    moo = element_get_matrix(cell_get(pc, 5, i));
    out[i]->fs = (unsigned int) matrix_get(moo, 0, 0);

  }
  
  return out;
}

void sentence_comp_features1(sentence *sen, matrix *(* trfunc)(matrix *, unsigned int, unsigned int, unsigned int))
{

  // Pokud jsou ve vete ulozeny priznaky tak predtim nez se prepisou tak se smazou.
  if (sen->features!=NULL) {
    matrix_free(sen->features);
  }

  unsigned int WIN, INC;
  WIN = round(sen->win*sen->fs);
  INC = round(sen->inc*sen->fs);

  sen->features = trfunc(sen->signal, WIN, INC, sen->fs);

  // Zjistim pocatky a konce slova priznaku.
  unsigned int i, beg, end, centre;
  for (i = 0; i<sen->nwords; i++) {
    sp_frame_location(sen->begining[i], sen->ending[i], &beg, &end, WIN, INC);
    sen->feature_beg[i] = beg;
    sen->feature_end[i] = end;
  }
  
}

void sentence_comp_features2(sentence **sen, unsigned int n, matrix *(* trfunc)(matrix *, unsigned int, unsigned int, unsigned int))
{
  unsigned int i;

  for (i =0; i<n; i++) {
    sentence_comp_features1(sen[i], trfunc);
  }
}

void sentence_mlp_transform1(sentence *sen, mlp *net)
{
  unsigned int i, j, mn, mf;
  matrix *old_features;
  if (sen->features==NULL) {
    printf("%s:%i: Veta nema vypocitane priznaky.\n", __FILE__, __LINE__);
  }
  mf = matrix_size_m(sen->features);
  mn = mlp_get_num_inputs(net);

  // Overeni jestli jde na priznaky vety pouzit neuronova sit NET.
  #ifdef DICTIONARY_DICTIONARY_DEBUG
  if (mf!=mn) {
    printf("%s:%i: Pro transformaci priznaku vety musi mit priznaky vety stejny pocet radku (%i) jako pocet vstupnich neuronu (%i).\n", mf, mn);
    exit(1);
  }
  #endif

  double *x, *y;
  x = malloc(sizeof(double)*mn);
  y = malloc(sizeof(double)*mlp_get_num_outputs(net));

  if (sen->features!=NULL) {
    old_features = sen->features;
  }
  matrix *mat = matrix_alloc(mlp_get_num_outputs(net), matrix_size_n(sen->features));

  for (i = 0; i<matrix_size_n(sen->features); i++) {

    for (j = 0; j<matrix_size_m(sen->features); j++) {
      x[j] = matrix_get(sen->features, j, i);
    }

    mlp_run(net, x, y);

    for (j = 0; j<matrix_size_m(mat); j++) {
      matrix_set(mat, j, i, y[j]);
    }
    
  }

  if (old_features!=NULL) {
    matrix_free(old_features);
  }

  sen->features = mat;
}


void sentence_mlp_transform2(sentence **sen, unsigned int n, mlp *net)
{
  unsigned int i;

  for (i =0; i<n; i++) {
    sentence_mlp_transform1(sen[i], net);
  }
}

matrix **sentence_stat_norm(sentence **sens, unsigned int N)
{
  unsigned int i, j, k, l, m, n;
  double value;
  matrix *avarge, *stdev, **OUT;
  matrix *features;

  // Predpoklada ze ve slovniku jsou vypocitane priznaky
  if (sens[0]->features==NULL) {
    fprintf(stderr, "%s: %i: Nejsou vypocitane priznaky slovniku.\n", __FILE__, __LINE__);
    exit(1);
  } else {
    m = matrix_size_m(sens[0]->features);
  }

  // Alokace vystupnich hodnot
  avarge = matrix_alloc(m, 1);
  stdev = matrix_alloc(m, 1);

  // Spocitani poctu priznaku
  n = 0;
  for (i = 0; i<N; i++) {
    n += matrix_size_n(sens[i]->features);
  }

  // Vypocet prumeru v jednotlivych radcich
  matrix_zeros(avarge);
  for (i = 0; i<N; i++) {
    features = sens[i]->features;
    for (k = 0; k<matrix_size_n(features); k++) {
      for (l = 0; l<m; l++) {
        matrix_set(avarge, l, 0, matrix_get(avarge, l, 0)+matrix_get(features, l, k));
      }
    }
  }
  for (i = 0; i<matrix_size_m(avarge); i++) {
    matrix_set(avarge, i, 0, matrix_get(avarge, i, 0) / n);
  }

  // Vypocet smerodatne odchylky
  matrix_zeros(stdev);
  for (i = 0; i<N; i++) {
    features = sens[i]->features;
    for (k = 0; k<matrix_size_n(features); k++) {
      for (l = 0; l<m; l++) {
        matrix_set(stdev, l, 0, matrix_get(stdev, l, 0)+pow(matrix_get(features, l, k)-matrix_get(avarge, l, 0), 2));
      }
    }
  }
  for (i = 0; i<matrix_size_m(avarge); i++) {
    matrix_set(stdev, i, 0, sqrt(matrix_get(stdev, i, 0) / n));
  }

  // Alokace vystupu
  OUT = (matrix **)malloc(sizeof(matrix*)*2);
  OUT[0] = avarge;
  OUT[1] = stdev;

  return OUT;
}

void sentence_stat_norm_aply(sentence *sen, matrix *avarge, matrix *stdev)
{

  if ((matrix_size_m(sen->features)!=matrix_size_m(avarge))|(matrix_size_m(avarge)!=matrix_size_m(stdev))) {
    fprintf(stderr, "%s: %i: Priznaky vety (%i x %i) i matice prumeru (%i x %i) a smerodatnych odchylek (%i x %i) musi mit stejny pocet radku.\n", __FILE__, __LINE__, matrix_size_m(sen->features), matrix_size_n(sen->features), matrix_size_m(avarge), matrix_size_n(avarge), matrix_size_m(stdev), matrix_size_n(stdev));
  }

  register unsigned int i, j;
  double value, av, st;

  for (i = 0; i<matrix_size_m(sen->features); i++) {
    av = matrix_get(avarge, i, 0);
    st = matrix_get(stdev, i, 0);
    for (j = 0; j<matrix_size_n(sen->features); j++) {
      value = matrix_get(sen->features, i, j);
      matrix_set(sen->features, i, j, (value-av)/st);
    }
  }

}

void sentence_stat_norm_aply2(sentence **sens, unsigned int n, matrix *avarge, matrix *stdev)
{

  unsigned int i;

  for (i = 0; i<n; i++) {
    sentence_stat_norm_aply(sens[i], avarge, stdev);
  }
}

void sentence_somp_projection(sentence *sen, matrix *basis)
{

  matrix *features, *new_features;
  features = sen->features;
  new_features = somp_base_projection(features, basis);
  matrix_free(features);
  sen->features = new_features;

}

void sentence_somp_projection2(sentence **sens, unsigned int n, matrix *basis)
{

  unsigned int i;

  for (i = 0; i < n; i++) {
    sentence_somp_projection(sens[i], basis);
  }
  
}

int sentence_find_word(sentence *sen, char *word, unsigned int k, unsigned int *beg, unsigned int *end)
{
  unsigned int i, j;
  j = 0;

  for (i = 0; i < sen->nwords; i++) {
    if (strcmp(sen->words[i], word) == 0) {
      // Slovo bylo nalezeno
      if (k == j) { // Pokud je nalezeno pozadovane K-te slovo WORD ve vete SEN

        if (beg != NULL) {
          *beg = sen->begining[i];
        }

        if (end != NULL) {
          *end = sen->ending[i];
        }
        
        return 1;
      }

      j++;

    }
  }

  // Slovo nebylo nalezeno
  return 0;
}

int sentence_is_word(sentence *sen, unsigned int pos, char *word, unsigned int tol)
{
  unsigned int k, beg;
  k = 0;
  while (sentence_find_word(sen, word, k++, &beg, NULL)) {
    if ((beg-tol<=pos)&(beg+tol>=pos)) {
      return 1;
    }
  }

  return 0;
}

int sentence_contein_word(sentence *sen, char *word)
{
  int i, k = 0;
  for (i = 0; i<sen->nwords; i++) {
    if (strcmp(sen->words[i], word)==0) {
      k++;
    }
  }
  return k;
}

matrix *sentence_word_process(sentence *sen, dictionary *dict, double tol)
{
  matrix *process = matrix_alloc(dict->n, matrix_size_n(sen->features));
  matrix_zeros(process);
  int i, j, k, found;
  unsigned int pos, beg, end, dh, I_word;
  for (i = 0; i<dict->n; i++) {

    I_word = 0;
    found = sentence_find_word(sen, dictionary_get_name(dict, i), I_word, &pos, NULL);
    while (found == 1) {
      dh = (int) round(dictionary_get_mean(dict, i)*((double) sen->inc)*tol*((double) sen->fs));
      sp_frame_location(pos-dh, pos+dh, &beg, &end, (unsigned int) round(sen->win*((double) sen->fs)), (unsigned int) round(sen->inc*((double) sen->fs)));

      for (k = beg; k<=end; k++) {
        matrix_set(process, i, k, 1);
      }
      I_word++;
      found = sentence_find_word(sen, dictionary_get_name(dict, i), I_word, &beg, &end);
    }

  }
  
  return process;
}

void sentence_print_info(sentence *sen)
{
  unsigned int i, j;
  double fs = (double) sen->fs;
  double length = ((double) matrix_size_m(sen->signal))/fs;
  printf("%s", sen->words[0]);
  for (i = 1; i<sen->nwords; i++) {
    printf(" %s", sen->words[i]);
  }
  if (sen->features!=NULL) {
    printf(". (%i words, %.2fs, signal : %i x %i, features : %i x %i, win/inc : %f/%f beg/end :", sen->nwords, length, matrix_size_m(sen->signal), matrix_size_n(sen->signal), matrix_size_m(sen->features), matrix_size_n(sen->features), sen->win, sen->inc);
    for (j = 0; j<sen->nwords; j++) {
      printf(" %i/%i", sen->feature_beg[j], sen->feature_end[j]);
    }
    printf("\n");
  } else {
    printf(". (%i words, %.2fs, signal : %i x %i , no features, win/inc : %f/%f, fs : %i, beg/end : ", sen->nwords, length, matrix_size_m(sen->signal), matrix_size_n(sen->signal), sen->win, sen->inc, sen->fs);
    for (j = 0; j<sen->nwords; j++) {
      printf(" %i/%i", sen->feature_beg[j], sen->feature_end[j]);
    }
    printf("\n");
  }
}

char *sentence_save_string (sentence *sen)
{
  long unsigned int L = 10;
  unsigned int i, j;
  char *pch;
  cell *pc;
  matrix *beg, *end, *fbeg, *fend, *fs, *win, *inc;
  beg = matrix_alloc(sen->nwords, 1);
  end = matrix_alloc(sen->nwords, 1);
  fbeg = matrix_alloc(sen->nwords, 1);
  fend = matrix_alloc(sen->nwords, 1);
  fs = matrix_alloc(1, 1);
  win = matrix_alloc(1, 1);
  inc = matrix_alloc(1, 1);

  // Pricteni delky signalu
  L += matrix_save_length(sen->signal);
  // Pricteni delky priznaku
  if (sen->features!=NULL) {
    L += matrix_save_length(sen->features);
  } else {
    L++;
  }


  for (i = 0; i<sen->nwords; i++) {
    L += (unsigned int) strlen(sen->words[i]) + 2; // Pricteni delky slova
    // Pricteni delky vsech fonemu daneho slova
    pc = sen->phonemes[i];
    for (j = 0; j<cell_size_n(pc); j++) {
      pch = element_get_string(cell_get(pc, 0, j));
      L += (unsigned int) strlen(pch)+1;
    }
    L++; // Pricteni odradkovani

    // Nastaveni hodnot zacatku a koncu
    matrix_set(beg, i, 0, sen->begining[i]);
    matrix_set(end, i, 0, sen->ending[i]);
    matrix_set(fbeg, i, 0, sen->feature_beg[i]);
    matrix_set(fend, i, 0, sen->feature_end[i]);

  }

  // Nastaveni frekvence
  matrix_set(fs, 0, 0, sen->fs);
  // Nastaveni vlastnosti oken
  matrix_set(win, 0, 0, sen->win);
  matrix_set(inc, 0, 0, sen->inc);

  // Pricteni delek pocatku
  L += matrix_save_length(beg)+1;
  L += matrix_save_length(end)+1;
  L += matrix_save_length(fbeg)+1;
  L += matrix_save_length(fend)+1;

  // Pricteni ostatnich delek frekvence a vlastnosti oken
  L += matrix_save_length(fs)+1;
  L += matrix_save_length(win)+1;
  L += matrix_save_length(inc)+1;

  char *string = malloc(sizeof(char)*((size_t) L));
  char buffer[200];

  // Inicializace retezce
  for (i = 0; i<L; i++) {
    string[i] = 0;
  }

  // Ulozeni poctu slov
  sprintf(buffer, "%i\n", sen->nwords);
  strcat(string, buffer);

  // Ulozeni slov
  for (i = 0; i<sen->nwords; i++) {

    sprintf(buffer, "%s\n", sen->words[i]);
    strcat(string, buffer);

  }

  // Ulozeni fonemu
  for (i = 0; i<sen->nwords; i++) {

    pc = sen->phonemes[i];
    for (j = 0; j<cell_size_n(pc)-1; j++) {
      sprintf(buffer, "%s ", element_get_string(cell_get(pc,0, j)));
      strcat(string, buffer);
    }
    sprintf(buffer, "%s\n", element_get_string(cell_get(pc,0, j)));
    strcat(string, buffer);

  }

  // Ulozeni signalu
  pch = matrix_save_save_string(sen->signal);
  strcat(string, pch);
  free(pch);
  strcat(string, "\n");

  // Ulozeni priznaku
  if (sen->features!=NULL) {
    pch = matrix_save_save_string(sen->features);
    strcat(string, pch);
    free(pch);
    strcat(string, "\n");
  } else {
    strcat(string, "n\n");
  }

  // Ulozeni zacatku a koncu
  pch = matrix_save_save_string(beg);
  strcat(string, pch);
  free(pch);
  strcat(string, "\n");
  pch = matrix_save_save_string(end);
  strcat(string, pch);
  free(pch);
  strcat(string, "\n");
  pch = matrix_save_save_string(fbeg);
  strcat(string, pch);
  free(pch);
  strcat(string, "\n");
  pch = matrix_save_save_string(fend);
  strcat(string, pch);
  free(pch);
  strcat(string, "\n");

  // Ulozeni frekvence a vlastnosti oken
  pch = matrix_save_save_string(fs);
  strcat(string, pch);
  free(pch);
  strcat(string, "\n");
  pch = matrix_save_save_string(win);
  strcat(string, pch);
  free(pch);
  strcat(string, "\n");
  pch = matrix_save_save_string(inc);
  strcat(string, pch);
  free(pch);
  strcat(string, "\n");

  // Uvolneni pomocnych matic
  matrix_free(beg);
  matrix_free(end);
  matrix_free(fbeg);
  matrix_free(fend);
  matrix_free(fs);
  matrix_free(win);
  matrix_free(inc);

  return string;

}


sentence *sentence_parse_string (char *string)
{

  unsigned int i, j, N, chars, spaces;
  size_t L;
  char buffer[200], *pch, *str;
  cell *phonemes;
  sscanf(string, "%i", &N);//printf("N = %i\n", N);
  // Alokace vysledne vety
  sentence *out = sentence_alloc(N);

  // Ulozeni slov
  for (i = 0; i<N; i++) {

    while (string[0]!=("\n")[0]) {string++;}string++;
    sscanf(string, "%s", buffer);
    L = strlen(buffer);
    pch = malloc(sizeof(char)*(L+1));
    memcpy(pch, string, L);
    pch[L] = 0;
    out->words[i] = pch;//printf("%s\n", out->words[i]);

  }
  
  // Ulozeni fonemu
  for (i = 0; i<N; i++) {

    while (string[0]!=("\n")[0]) {string++;}string++;
    //printf("%.10s\n", string);

    phonemes = dictionary_phoneme_parse(string);//printf("phonemes %i\n", phonemes->n);
    out->phonemes[i] = phonemes;

  }

  // Nacteni signalu a jeho ulozeni
  while (string[0]!=("\n")[0]) {string++;}string++;
  out->signal = matrix_load_string(string);
  //matrix_print(out->signal);
  //printf("signal (%i x %i) energy = %f\n", out->signal->m, out->signal->n, matrix_get(sp_signal_energy(out->signal), 0, 0));

  // Nacteni priznaku
  while (string[0]!=("\n")[0]) {string++;}string++;
  if (string[0]=="n"[0]) {
    while (string[0]!=("\n")[0]) {string++;}string++;
  } else {
    out->features = matrix_load_string(string);
    while (string[0]!=("\n")[0]) {string++;}string++;
  }

  // Nacteni zacatku a koncu slov, frekvence a vlastnosti oken
  matrix *beg, *end, *fbeg, *fend, *fs, *win, *inc;
  beg = matrix_load_string(string);//puts("begining");matrix_print(beg);
  while (string[0]!=("\n")[0]) {string++;}string++;
  end = matrix_load_string(string);//puts("ending");matrix_print(end);
  while (string[0]!=("\n")[0]) {string++;}string++;
  fbeg = matrix_load_string(string);//puts("fbeg");matrix_print(fbeg);
  while (string[0]!=("\n")[0]) {string++;}string++;
  fend = matrix_load_string(string);//puts("fend");matrix_print(fend);
  while (string[0]!=("\n")[0]) {string++;}string++;
  fs = matrix_load_string(string);//puts("fs");matrix_print(fs);
  while (string[0]!=("\n")[0]) {string++;}string++;
  win = matrix_load_string(string);//puts("win");matrix_print(win);
  while (string[0]!=("\n")[0]) {string++;}string++;
  inc = matrix_load_string(string);//puts("inc");matrix_print(inc);

  // Ulozeni zacatku a koncu slov
  for (i = 0; i<N; i++) {
    out->begining[i] = (unsigned int) matrix_get(beg, i, 0);
    out->ending[i] = (unsigned int) matrix_get(end, i, 0);
    out->feature_beg[i] = (unsigned int) matrix_get(fbeg, i, 0);
    out->feature_end[i] = (unsigned int) matrix_get(fend, i, 0);
  }

  // Ulozeni frekvence a vlastnosti oken
  out->fs = (unsigned int) matrix_get(fs, 0, 0);
  out->win = matrix_get(win, 0, 0);
  out->inc = matrix_get(inc, 0, 0);

  // Uvolneni nepotrebnych promenych
  matrix_free(beg);
  matrix_free(end);
  matrix_free(fbeg);
  matrix_free(fend);
  matrix_free(fs);
  matrix_free(win);
  matrix_free(inc);

  return out;
  
}



void sentences_save (const char *filename, sentence **sens, unsigned int n)
{
  long unsigned int N = 0;
  char buffer[10], **strings = malloc(sizeof(char)*n);
  unsigned int i;
  FILE *pfile = fopen(filename, "w");
  sprintf(buffer, "%i\n", n);
  fputs(buffer, pfile);
  fclose(pfile);

  // Zjisteni potrebne delky retezce.
  for (i = 0; i<n; i++) {
    printf("%i/%i\n", i+1, n);
    strings[i] = sentence_save_string(sens[i]);
    N = strlen(strings[i])+1;
  }

  // Ulozeni vet do souboru.
  for (i = 0; i<n; i++) {
    pfile = fopen(filename, "a");
    fputs(strings[i], pfile);
    fclose(pfile);
  }

}


sentence **sentences_load (const char *filename, unsigned int *n, unsigned int max)
{
  FILE * pFile;
  long lSize;
  char * str;
  size_t result;

  pFile = fopen ( filename , "rb" );
  if (pFile==NULL) {printf("%s:%i: Nepodarilo se nacist soubor \"%s\".\n", __FILE__, __LINE__, filename); exit (1);}

  // obtain file size:
  fseek (pFile , 0 , SEEK_END);
  lSize = ftell (pFile);
  rewind (pFile);

  // allocate memory to contain the whole file:
  str = (char*) malloc (sizeof(char)*lSize);
  if (str == NULL) {fputs ("Memory error",stderr); exit (2);}

  fread(str, 1, lSize, pFile);

  //printf("%s\n", str);

  *n = atoi(str);
  if (*n>max) {
    *n = max;
  }
  //printf("n = %i\n", *n);
  sentence **sens = malloc(sizeof(sentence)*(*n));
  unsigned int i, j, k;
  while(str[0]!=("\n")[0]) { str++; } str++;

  for (i = 0; i<*n & i<max; i++) {

    sens[i] = sentence_parse_string(str);
    sentence_print_info(sens[i]);
    k = sens[i]->nwords*2+10;

    for (j = 0; j<k; j++) {
      while(str[0]!=("\n")[0]) { str++; } str++;
    }

    //printf("%30s", str);

  }

  return sens;

}