#include "detector.h"

detector_window *detector_window_alloc (unsigned int n)
{
  detector_window *dw = malloc(sizeof(detector_window));
  dw->frames = list_alloc();
  dw->size = n;
  return dw;
}

void detector_window_free (detector_window *df)
{
  list_free(df->frames);
  free(df);
}

int detector_window_add (detector_window *dw, matrix *frame, detector_list_opt *opt)
{
  if (list_get_size(dw->frames) < opt->max_frames) {
    #ifdef DETECTOR_DEBUG
    if (matrix_size_n(frame)>1) {
      printf("%s:%i: error: Matice okna musi mit maximalne jeden slopec (%i)./n", __FILE__, __LINE__, matrix_size_n(frame));
      return 0;
    }
    #endif
    list_add_last(dw->frames, element_alloc_matrix(frame));
    return 1;
  } else {
    list_add_last(dw->frames, element_alloc_matrix(frame));
    list_first(dw->frames);
    list_free_curent(dw->frames, 0);
    return 1;
  }
  return 0;
}

matrix *detector_class_distances (dictionary_class *word_class, detector_window *dw, detector_list_opt *opt, unsigned int K)
{
  unsigned int i, j, Gn, frames;
  matrix *pattern, *G, *utterence; // vzor z tridy a matice kumulovane vzdalenosti
  // Matice vzdalenosti oken od jednotlivych promluv
  matrix *D = matrix_alloc(word_class->n, opt->max_frames - opt->min_frames + 1);

  // Vypocet matice vzdalenosti
  for (i = 0; i<word_class->n; i++) { // Pro kazdy vzor ze tridy WORD_CLASS.

    pattern = dictionary_class_get_features(word_class, i); // Vzor z tridy.
    utterence = matrix_cat_list(dw->frames, MATRIX_CAT_HORIZONTALLY);
    G = dtw2(pattern, utterence); // vzdalenost promluvy a vzoru.
    matrix_free(utterence);
    Gn = matrix_size_n(G);

    // Ulozeni sloupce (vzdalenost slovni tridy od promluvy v kazdem ramci)
    j = 0;
    for (frames = opt->min_frames - 1; frames < opt->max_frames; frames++) { // pro vsechny delky ramcu
      //printf("G.n = %i , frames = %i\n", Gn, frames);
      if (Gn>=frames) { // Pokud ma promluva min nebo stejne ramcu jako pozadovane okno
        matrix_set(D, i, j, matrix_get(G, matrix_size_m(G)-1, frames)); // Ulozeni vzdalenosti.
      } else {
        matrix_set(D, i, j, matrix_size_m(pattern));  // Okno je vetsi nez promluva
      }
      j++;
    }

    matrix_free(G);

  }
  // Vypocet nejmensi vzdalenosti pro kazde okno vuci slovni tride WORD_CLASS.
  matrix *out = detector_k_nn(D, K);
  matrix_free(D); // Uvolneni matice vzdalenosti promluv ze slovni tridy k jednotlivym oknum.

  return out; // Vrati matici vzdalenosti k dane tride.
}

matrix * detector_k_nn (matrix *cl_dist, unsigned int K)
{
  #ifdef DETECTOR_DEBUG
  if (K==0) {
    printf("%s:%i:error: Nelze vypocitat vzdalenost pomoci K nejmensich sousedu kdyz K je 0.\n", __FILE__, __LINE__);
  }
  #endif

  unsigned int w, i, M, found, min_count;
  double minimum, last_minimum, value, K_dist;
  matrix *out = matrix_alloc(1, matrix_size_n(cl_dist));

  M = matrix_size_m(cl_dist);
  if (K>M) {
    #ifdef DETECTOR_DEBUG
    printf("%s:%i:WARNING: K je vetsi nez pocet vzoru ve tride (%i > %i), proto bude K = %i.\n", __FILE__, __LINE__, K, M, M);
    #endif
    K = M;
  }
  
  for (w = 0; w<matrix_size_n(cl_dist); w++) {

    last_minimum = -DETECTOR_INFINITY; // minimum z minuleho cyklu
    minimum = DETECTOR_INFINITY; // minimum ze soucasneho cyklu je vetsi jak minimum z minuleho cyklu
    found = 0; // pocet nalezenych minim
    K_dist = 0; // soucet nejmensich vzdalenosti

    while (found<K) { // Dokud se nenalezne K nejmensich hodnot

      // Prohledani matice CL_DIST
      for (i = 0; i<M; i++) {
        value = matrix_get(cl_dist, i, w);

        // Pokud je nalezeno minimum
        if ((value<=minimum)&(value>last_minimum)) {
          if (value<minimum) {
            min_count = 0; // Inicializace poctu nalezenych minim
          }
          minimum = value;
          min_count++;
        }

      }

      found += min_count;

      if (found<=K) {
        K_dist += minimum*min_count;
      } else {
        K_dist += minimum*(min_count-(found-K));
      }

      last_minimum = minimum;
      minimum = DETECTOR_INFINITY;

    }
    
    matrix_set(out, 0, w, K_dist/K);

  }

  return out;
}


matrix *detector_dict_distance (dictionary *dict, detector_window *dw, detector_list_opt *opt, unsigned int K)
{
  unsigned int i, j;
  dictionary_class *word_class;
  // Vystupni matice. sloupce - jednotliva okna, radky - vzdalenosti trid od oken.
  matrix * out = matrix_alloc(dict->n, opt->max_frames - opt->min_frames + 1);
  matrix * cldist; // radkova matice vzdalenosti slovni tridy od okna.

  // Vypocet matice vzdalenosti slovnich trid od oken.
  for (i = 0; i<dict->n; i++) { // peo vsechny slovni tridy.
    word_class = dictionary_get(dict, i); // dana slovni trida.
    cldist = detector_class_distances(word_class, dw, opt, K); // vzdalenost oken od dane slovni tridy.
    //matrix_print(cldist);
    for (j = 0; j < matrix_size_n(out); j++) {
      matrix_set(out, i, j, matrix_get(cldist, 0, j)); // nastaveni konkretni vzdalenosti.
    }
    matrix_free(cldist);

  }

  // Navratova matice vzdalenosti slovnich trid ze slovniku od jednotlivych oken.
  return out;
}

matrix *detector_distance_weight_multiply (matrix *dist, matrix *weight)
{
  unsigned int md, nd, mw, nw;
  md = matrix_size_m(dist);
  nd = matrix_size_n(dist);
  mw = matrix_size_m(weight);
  nw = matrix_size_n(weight);
  // Kontrola jestli maji obe matice stejne rozmery
  #ifdef DETECTOR_DEBUG
  if ((md!=mw)|(nd!=nw)) {
    printf("%s:%i: Vaovaci matice (%i x %i) ma jine rozmery nez matice vzdalenosti slov k jednotlivym triddam (%i x %i),\n", __FILE__, __LINE__, mw, nw, md, nd);
    exit(1);
  }
  #endif

  // Vypocet konecnych vzdalenosti trid od dane pozice v signalu.
  double d;
  matrix *dwout = matrix_alloc(md, 1);
  unsigned int i, j;
  for (i = 0; i<md; i++) {

    d = 0;
    for (j = 0; j<nd; j++) {
      d += matrix_get(dist, i, j)*matrix_get(weight, i, j);
    }
    matrix_set(dwout, i, 0, d);
    
  }

  return dwout;
  
}


matrix **detector_words_distances (matrix *frames, dictionary *dict, detector_list_opt *opt, unsigned int step, unsigned int K, unsigned int *n)
{
  unsigned int i, j, k, index;
  // Alokace klouzaveho okna podle nastaveni OPT
  detector_window *dw = detector_window_alloc(opt->max_frames - opt->min_frames + 1);
  matrix *FRAME, *DICT_DIST;
  // Vypocet poctu kroku
  if (matrix_size_n(frames) >= opt->max_frames) {
    *n = floor((double)(matrix_size_n(frames) - opt->max_frames -step + 1)/((double)step))+1;
  } else {
    // Je potreba delsi signal pro vyhodnoceni vzdalenosti mezi jednotlivymi
    // slovy ze slovniku a oknami ze signálu
    *n = 0;
    return NULL;
  }
  // Alokace vystupniho pole matic
  matrix **word_distance = (matrix**)malloc(sizeof(matrix*)*(*n));//printf("%i %i opt.n=%i dict.n=%i\n", matrix_size_m(word_distance), matrix_size_n(word_distance), opt->n, dict->n);

  for (j = 0; j<opt->max_frames; j++) {
    FRAME = matrix_alloc(matrix_size_m(frames), 1);
    for (k = 0; k<matrix_size_m(frames); k++) {
      matrix_set(FRAME, k, 0, matrix_get(frames, k, j));
    }

    detector_window_add(dw, FRAME, opt);
  }

  index = opt->max_frames+step-1;
  i = 0;
  
  do {

    DICT_DIST = detector_dict_distance(dict, dw, opt, K);
    word_distance[i] = DICT_DIST;//matrix_print_info(DICT_DIST);
    //matrix_print(DICT_DIST);

    if (i < *n) {
      for (j = 0; (j<step)&(index-j<matrix_size_n(frames)); j++) {
        FRAME = matrix_alloc(matrix_size_m(frames), 1);
        for (k = 0; k<matrix_size_m(frames); k++) {
          matrix_set(FRAME, k, 0, matrix_get(frames, k, index-step+j));
        }

        detector_window_add(dw, FRAME, opt);
      }
    }
    
    index += step;
    i++;

  } while (index < matrix_size_n(frames));

  DICT_DIST = detector_dict_distance(dict, dw, opt, K);
  word_distance[i] = DICT_DIST;

  // Uvolneni pomocnych promenych
  detector_window_free(dw);
  return word_distance;
}


matrix *detector_word_proces(matrix **D, unsigned int n, matrix *weights)
{
  unsigned int i, j;//printf("n = %i\n", n);
  matrix *out = matrix_alloc(matrix_size_m(weights), n);
  matrix *dist;

  for (i = 0; i<n; i++) {
    dist = detector_words_value(D[i], weights);
    for (j = 0; j<matrix_size_m(weights); j++) {
      matrix_set(out, j, i, matrix_get(dist, j, 0));
    }
    matrix_free(dist);
  }

  return out;
}

matrix *detector_word_process(matrix *sen, dictionary *dict, detector_list_opt *opt, unsigned int step, unsigned int K, matrix *weights)
{
  unsigned int i, n;
  if (matrix_size_n(sen)>0) {
    matrix **word_distances = detector_words_distances(sen, dict, opt, step, K, &n);
    matrix *process = detector_word_proces(word_distances, n, weights);
    for (i = 0; i < n; i++) {
      matrix_free(word_distances[i]);
    }
    return process;
  } else {
    fprintf(stderr, "%s:%i: WARNING: Příznaky vety nejsou vypočítány", __FILE__, __LINE__);
    return NULL;
  }
}

matrix *detector_word_process_1(sentence *sen, dictionary *dict, detector_list_opt *opt, unsigned int step, unsigned int K, matrix *weights)
{

  if (sen->features != NULL) {
    return detector_word_process(sen->features, dict, opt, step, K, weights);
  } else {
    fprintf(stderr, "%s:%i: ERROR: Příznaky vety nejsou vypočítány", __FILE__, __LINE__);
    exit(1);
  }
}

matrix **detector_word_process_2(sentence **sens, unsigned int n, dictionary *dict, detector_list_opt *opt, unsigned int step, unsigned int K, matrix *weights)
{
  matrix **out = malloc(sizeof(matrix *) * n);
  unsigned int i;

  puts("process");
  for (i = 0; i < n; i++) {
  }
  
  for (i = 0; i < n; i++) {
    //puts("A");
    out[i] = detector_word_process_1(sens[i], dict, opt, step, K, weights);
    printf("%i/%i\n", i+1, n);
  }
  
  return out;
}

matrix * detector_words_value (matrix *word_distance, matrix *weight)
{
  //puts("vahy");printf("%i\n", matrix_size_m(weight));printf("%i\n", matrix_size_n(weight));puts("slovni vzdalenosti");printf("%i\n", matrix_size_m(word_distance));printf("%i\n", matrix_size_n(word_distance));
  
  // Zkontroluje jestli maji obe matice stejne rozmery.
  if ((matrix_size_m(word_distance)!=matrix_size_m(weight))|(matrix_size_n(word_distance)!=matrix_size_n(weight))) {
    printf("%s:%i: error: Vahovaci matice (%i x %i) ma jine rozmery nez matice vzdalenosti slovnich trid k ramcum (%i %i)\n", __FILE__, __LINE__, matrix_size_m(weight), matrix_size_n(weight), matrix_size_m(word_distance), matrix_size_n(word_distance));
    exit(1);
  }
  unsigned int i, j, m, n;
  // Rozmery matic (jsou stejne pokud se to dostalo az sem)
  m = matrix_size_m(word_distance);
  n = matrix_size_n(word_distance);
  matrix *out = matrix_alloc(m, 1);
  double value;
  for (i = 0; i<m; i++) {
    value = 0;  // hodnota daneho slova
    for (j = 0; j<n; j++) {
      value += matrix_get(word_distance, i, j)*matrix_get(weight, i, j); // inkrementace hodnoty daneho slova podle vahy k danem ramci a ramce
    }
    matrix_set(out, i, 0, value); // nastaveni hodnoty slova
  }
  // navratova hodnota
  return out;
}


matrix *detector_word_detect (matrix *process, dictionary *dict, matrix *treshold)
{
  /*
   * TADY DODELAT OSETRENI DAT !!!!!!!!!!!
   */
  matrix *detections = matrix_alloc(dict->n, matrix_size_n(process));
  matrix *out = matrix_alloc(dict->n, matrix_size_n(process));
  matrix_zeros(out);
  unsigned int i, j, k, n, I_min;
  int state;
  double min;

  // Algoritmus detekce slov pomoci prahovch hodnot
  for (i = 0; i<matrix_size_n(process); i++) {
    for (j = 0; j<matrix_size_m(process); j++) {
      if (matrix_get(treshold, 0, j) >= matrix_get(process, j, i)) {
        // DETEKCE
        matrix_set(detections, j, i, 1);
      } else {
        matrix_set(detections, j, i, 2);
      }
    }
  }

  for (i = 0; i<matrix_size_m(out); i++) {
    for (j = 0; j<matrix_size_n(out); j++) {
      
      state = (int) matrix_get(detections, i, j);

      // Pokud je posloupnist jednicek tak necham jen tu ktera ma v PROCESS
      // minimalni hodnotu na stejne pozici
      if (state == 1) {
        // Zjistim velikost posloupnisti jednicek
        n = 0;
        do {
          state = (int) matrix_get(detections, i, j+n);
          n++;
        } while ((state==1) & (j+n<matrix_size_n(detections)));
        n--;
        
        // Nalezeni minima
        I_min = j;
        min = matrix_get(detections, i, I_min);
        for (k = 1; k<=n & k+j<matrix_size_n(detections); k++) {
          if (matrix_get(process, i, j+k) < min) {
            I_min = j+k;
            min = matrix_get(process, i, I_min);
          }
        }

        // Nastaveni detekovaneho slova
        matrix_set(out, i, I_min, 1);
        j = j+n; // V dalsim cyklu se bude upravovat posloupnost jednicek od tohoto indexu

      }

    }

  }

  matrix_free(detections);
  return out;
}

matrix *detector_determine_treshold(sentence *sen, dictionary *dict, detector_list_opt *opt, unsigned int step, unsigned int K, mlp *net)
{
  unsigned int i, j, k, n, N, fbeg, fend, length;
  matrix *process;
  matrix *treshold;
  double value;
  double max[dict->n];
  double min[dict->n];

  // Vynuluju max
  for (i = 0; i<dict->n; i++) {
    max[i] = 0;
  }

  treshold = matrix_alloc(1, dict->n);
  // Prubehy jednotlivych vzdalenosti
  process = detector_words_distances(sen->features, dict, opt, step, K, &N);

  // Projdu vsechny slova ve vete a zjistim jestli jsou ve slovniku
  for (i = 0; i<dict->n; i++) {

    for (j = 0; j<sen->nwords; j++) {

      // Pokud dane slovo je ve vete
      if (strcmp(sen->words[j], dictionary_get_name(dict, i))==0) {
        // Poloha slova
        fend = sen->feature_end[i];
        // Hodnota vzdalenosti
        value = matrix_get(process, i, j);
        if (max[i]<value) {
          max[i] = value;
        }

      }

    }

  }

  for (i = 0; i<dict->n; i++) {
    matrix_set(treshold, 0, i, max[i]);
  }

  return treshold;

}


matrix *detector_treshold(sentence **sen, unsigned int N, dictionary *dict, detector_list_opt *opt, matrix *weight, unsigned int step, unsigned int K, mlp *net)
{
  unsigned int i, j;
  matrix **tresholds = (matrix **)malloc(sizeof(matrix *)*N);
  matrix *treshold_out = matrix_alloc(1, dict->n);
  double value, foo;

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

    tresholds[i] = detector_determine_treshold(sen[i], dict, opt, step, K, net);
    matrix_print(tresholds[i]);
    
  }

  for (i = 0; i<dict->n; i++) {
    value = 0;
    for (j = 0; j<N; j++) {
      foo = matrix_get(tresholds[j], 0, i);
      if (foo>value) {
        value = foo;
        matrix_set(treshold_out, 0, i, foo);
      }
    }
  }

  for (i = 0; i<N; i++) {
    matrix_free(tresholds[i]);
  }

  return treshold_out;

}

matrix *detector_treshold_stats(sentence *sen, dictionary *dict, detector_list_opt *opt, unsigned int step, unsigned int K, mlp *net, matrix *weights)
{

  matrix *stats = matrix_alloc(2, dict->n);
  int i, j, k, fbeg, fend, N, a;
  double value, foo;

  // Prubehy jednotlivych vzdalenosti
  matrix **distances = detector_words_distances(sen->features, dict, opt, step, K, &N);

  matrix *process = detector_word_proces(distances, N, weights);

  //matrix_print(matrix_transpose(process));

  for (i = 0; i<dict->n; i++) {

    matrix_set(stats, 0, i, matrix_get(process, i, 0));


    for (j = 0; j<sen->nwords; j++) {

      // Pokud dane slovo je ve vete
      if (strcmp(sen->words[j], dictionary_get_name(dict, i))==0) {
        // Poloha slova
        fbeg = sen->feature_beg[j];
        fend = sen->feature_end[j];
        //printf("%s %i %i\n", sen->words[j], fbeg, fend);

        value = DETECTOR_INFINITY;
        if (fend - opt->max_frames >= 0) {
          a = fend - opt->max_frames;
        } else {
          a = 0;
        }
        // Minimalni hodnota vzdalenosti slov v danem useku
        for (k = a; (k<fend - opt->min_frames)&(k<matrix_size_n(process)); k++) {
          foo = matrix_get(process, i, k);
          if (foo<value) {
            value = foo;
          }
        }
        matrix_set(stats, 0, i, value);

        // Nalezeni minima mimo oblast slova
        // VLEVO
        value = DETECTOR_INFINITY;
        for (k = 0; k<a; k++) {
          foo = matrix_get(process, i, k);
          if (foo<value) {
            value = foo;
          }
        }
        // VPRAVO
        for (k = fend; k<matrix_size_n(process); k++) {
          foo = matrix_get(process, i, k);
          if (foo<value) {
            value = foo;
          }
        }
        matrix_set(stats, 1, i, value); // minimalni vzdalenost slova mimo svou polohu ve vete

      }

    }

  }

  matrix_free(process);

  return stats;

}

matrix **detector_treshold_stats2(sentence **sens, unsigned int n, dictionary *dict, detector_list_opt *opt, unsigned int step, unsigned int K, mlp *net, matrix *weights)
{
  unsigned int i;
  matrix **stats = (matrix *)malloc(sizeof(matrix *)*n);

  for (i = 0; i<n; i++) {
    stats[i] = detector_treshold_stats(sens[i], dict, opt, step, K, net, weights);
    //puts("stats");matrix_print(stats[i]);
  }

  return stats;
}


matrix *detector_design_treshold(matrix **stats, unsigned int n)
{
  unsigned int i, j;
  matrix *min, *max, *out;
  double valmin, valmax;

  min = matrix_alloc(1, matrix_size_n(stats[0]));
  max = matrix_alloc(1, matrix_size_n(stats[0]));
  for (i = 0; i<matrix_size_n(min); i++) {
    matrix_set(max, 0, i, DETECTOR_INFINITY);
  }
  matrix_zeros(min);
  
  for (i = 0; i<n; i++) {
    for (j = 0; j<matrix_size_n(min); j++) {
      valmin = matrix_get(stats[i], 0, j);
      valmax = matrix_get(stats[i], 1, j);
      if (valmin<valmax) { // Pokud se ve vete nevyskytuje situace kdy mimo hranice slova je vzdaleost ke slovu mensi nez uvnitr hranice
        if (valmin>matrix_get(min, 0, j)) {
          matrix_set(min, 0, j, valmin);
        }
        if (valmax<matrix_get(max, 0, j)) {
          matrix_set(max, 0, j, valmax);
        }
      }
    }
    //printf("%i\n", i);
    //matrix_print(stats[i]);
    //matrix_print(min);
    //matrix_print(max);
  }

  out = matrix_alloc(1, matrix_size_n(max));

  for (j = 0; j<matrix_size_n(out); j++) {
      matrix_set(out, 0, j, (matrix_get(min, 0, j)+matrix_get(max, 0, j))/2);
      //printf("%f/2 + %f/2 = %f\n", matrix_get(min, 0, j), matrix_get(max, 0, j), matrix_get(out, 0, j));
  }

  matrix_free(min);
  matrix_free(max);

  return out;
 
}


matrix *detector_WER_Insections(matrix *word_process, matrix *detection)
{
  unsigned int i, j, k, sum_I;
  matrix *I_mat = matrix_alloc(matrix_size_m(word_process), 1);
  matrix_zeros(I_mat);
  for (i = 0; i<matrix_size_m(word_process); i++) {
    for (j = 0; j<matrix_size_n(word_process); j++) {
      if (matrix_get(word_process, i, j)==1) {
        sum_I = 0;
        for (k = j; k<matrix_size_n(word_process); k++) {
          if (matrix_get(word_process, i, k)!=1) {
            break;
          }
          if (matrix_get(detection, i, k)==1) {
            sum_I++;
          }
        }
        j = k-1;
        if (sum_I>1) {
          matrix_set(I_mat, i, 0, matrix_get(I_mat, i, 0)+sum_I-1);
        }
      }

    }
  }
  return I_mat;
}


matrix *detector_WER_Deletions(matrix *word_process, matrix *detection)
{
  unsigned int i, j, k, sum_D;
  matrix *D_mat = matrix_alloc(matrix_size_m(word_process), 1);
  matrix_zeros(D_mat);
  for (i = 0; i<matrix_size_m(word_process); i++) {
    for (j = 0; j<matrix_size_n(word_process); j++) {
      if (matrix_get(word_process, i, j)==1) {
        sum_D = 0;
        for (k = j; k<matrix_size_n(word_process); k++) {
          if (matrix_get(word_process, i, k)!=1) {
            break;
          }
          if (matrix_get(detection, i, k)==1) {
            sum_D++;
          }
        }
        j = k-1;
        if (sum_D==0) {
          matrix_set(D_mat, i, 0, matrix_get(D_mat, i, 0)+1);
        }
      }
    }
  }
  return D_mat;
}

matrix *detector_WER_ISD(matrix *word_process, matrix *detection)
{
  // Zjisteni poctu pridanych slov navic v techto intervalech
  matrix *word_process_NEG = matrix_neg(word_process);
  matrix *S_mat = matrix_and(word_process_NEG, detection);
  matrix *SS = matrix_sum_horizontaly(S_mat);
  matrix *II = detector_WER_Insections(word_process, detection);
  matrix_free(word_process_NEG);
  matrix_free(S_mat);

  // Pocet vynechanych slov
  matrix *DD = detector_WER_Deletions(word_process, detection);

  // Ulozeni do vystupu
  list *L = list_alloc();
  list_add_last(L, element_alloc_matrix(II));
  list_add_last(L, element_alloc_matrix(SS));
  list_add_last(L, element_alloc_matrix(DD));
  matrix *OUT = matrix_cat_list(L, MATRIX_CAT_HORIZONTALLY);
  list_free(L);
  return OUT;
}


matrix *detector_quick_WER(matrix *detection, sentence *sen, dictionary *dict, double tol)
{
  int i;
  matrix *wp = sentence_word_process(sen, dict, tol);

  if (matrix_size_n(wp) < matrix_size_n(detection)) { // Pokud nesedi rozmery
    printf("%s: %i: ERROR: Matice prubehu slov je kratsi nez matice prubehu detekovanych slov.\n");
    exit(1);
  }
  matrix *word_process = matrix_submatrix(wp, 0, matrix_size_m(wp)-1, 0, matrix_size_n(detection)-1);
  matrix_free(wp);
  matrix *NN = matrix_alloc(dict->n, 1);

  // Zjisteni poctu vyskytu klicovych slov
  for (i = 0; i<dict->n; i++) {
    matrix_set(NN, i, 0, (double) sentence_contein_word(sen, dictionary_get_name(dict, i)));
  }

  list *L = list_alloc();
  list_add_last(L, element_alloc_matrix(detector_WER_ISD(word_process, detection)));
  list_add_last(L, element_alloc_matrix(NN));
  matrix *OUT = matrix_cat_list(L, MATRIX_CAT_HORIZONTALLY);
  list_free(L);
  return OUT;
}

matrix *detector_quick_WER_2(matrix **process, sentence **sens, unsigned int n, dictionary *dict, matrix *treshold, double tol)
{
  unsigned int i, j;
  matrix *detection;
  matrix *ISDN, *OUT, *foo;
  detection = detector_word_detect(process[0], dict, treshold);
  OUT = detector_quick_WER(detection, sens[0], dict, tol);
  matrix_free(detection);
  for (i = 1; i < n; i++) {
    detection = detector_word_detect(process[i], dict, treshold);
    ISDN = detector_quick_WER(detection, sens[i], dict, tol);
    foo = matrix_adition(ISDN, OUT);
    matrix_free(OUT);
    matrix_free(ISDN);
    OUT = foo;
    matrix_free(detection);
    //for (j = 0; j<1000; j++) {};
  }
  
  return OUT;
}

double detector_WER(matrix *ISDN)
{
  matrix *M = matrix_sum_verticaly(ISDN);
  double I = matrix_get(M, 0, 0);
  double S = matrix_get(M, 0, 1);
  double D = matrix_get(M, 0, 2);
  double N = matrix_get(M, 0, 3);
  matrix_free(M);
  return (I+S+D)/N;
}

matrix *detector_process_stats(matrix **process, unsigned int n)
{
  unsigned int i, j, k;
  double value, globalMax, globalMin;
  matrix *stats = matrix_alloc(matrix_size_m(process[0]), 2);
  for (k = 0; k<matrix_size_m(process[0]); k++) {
    matrix_set(stats, k, 0, matrix_get(process[0], k, 0));
    matrix_set(stats, k, 1, matrix_get(process[0], k, 0));
  }
  for (i = 0; i<n; i++) {
    for (j = 0; j<matrix_size_m(process[i]); j++) {
      globalMin = matrix_get(stats, j, 0);
      globalMax = matrix_get(stats, j, 1);
      for (k = 0; k<matrix_size_n(process[i]); k++) {
        value = matrix_get(process[i], j, k);
        if (value<globalMin) {
          globalMin = value;
          matrix_set(stats, j, 0, globalMin);
        }
        if (value>globalMax) {
          globalMax = value;
          matrix_set(stats, j, 1, globalMax);
        }
      }
    }
  }
  return stats;
}

matrix **detector_treshold_try(matrix **process, sentence **sens, unsigned int n, matrix *tresholdMin, matrix *tresholdMax, dictionary *dict, unsigned int n_tests, double tol)
{

  matrix *best_treshold, *swap, *treshold, *ISDN;
  unsigned int i, j;
  int pos, neg, miss, best_pos, best_neg, best_miss;
  double scoore, best_scoore, min, max, test_value, range;

  treshold = matrix_clone(tresholdMax);
  best_treshold = matrix_clone(tresholdMax);
  ISDN = detector_quick_WER_2(process, sens, n, dict, treshold, tol);
  scoore = best_scoore = detector_WER(ISDN);
  matrix_free(ISDN);

  // Vyzkousim vsechyn sova ve slovniku
  for (i = 0; i < dict->n; i++) {

    max = matrix_get(tresholdMax, 0, i); // minimalni hranice prahu daneho slova
    min = matrix_get(tresholdMin, 0, i); // maximalni hranice prahu daneho slova
    range = max-min;                     // Rozsah prahu
    
    // Testovani hodnot mezi MIN a MAX
    for (j = 0; j < n_tests; j++) {

      test_value = max - ((double) j)*range/((double) n_tests); // vypocet testovaci hodnoty prahu
      //printf("%i %f %f %f\n", j, max, test_value, min);

      // Testovani prahu s hodnotou TEST_VALUE
      matrix_set(treshold, 0, i, test_value);

      ISDN = detector_quick_WER_2(process, sens, n, dict, treshold, tol);
      scoore = detector_WER(ISDN);
      matrix_free(ISDN);

      // Pokud bylo nalezeno lepsi reseni
      if (scoore < best_scoore) {
        matrix_set(best_treshold, 0, i, test_value);
        best_scoore = scoore;
        best_pos = pos;
        best_neg = neg;
        best_neg = miss;
      } else {
        matrix_free(treshold);
        treshold = matrix_clone(best_treshold);
      }

    }

  }

  matrix_free(treshold);
  matrix_free(tresholdMax);
  matrix_free(tresholdMin);
  return best_treshold;

}

