/*
 *  LevenshteinDynamicProgramming.h
 *
 *  Created on: 1.5.2018
 *      Author: Tomas Capek
 */

#pragma once

#include <algorithm>
#include <limits.h>

#include <string/LinearString.h>

namespace stringology {

namespace simulations {

class GeneralizedLevenshteinDynamicProgramming {
public:
    template <class SymbolType>
    static ext::vector<ext::vector<unsigned int>> compute_table(const string::LinearString<SymbolType> & text, const string::LinearString<SymbolType> & pattern);

    template <class SymbolType>
    static ext::set<unsigned int> search(const string::LinearString<SymbolType> & text, const string::LinearString<SymbolType> & pattern, unsigned int errors);
};

template <class SymbolType>
ext::vector<ext::vector<unsigned int>> GeneralizedLevenshteinDynamicProgramming::compute_table(const string::LinearString<SymbolType> & text, const string::LinearString<SymbolType> & pattern) {
  ext::vector< ext::vector <unsigned int> > table =
    ext::vector<ext::vector<unsigned int> > (
      pattern.getContent().size() + 1,
      ext::vector<unsigned int>(text.getContent().size() + 1, 0)
    );

  for(unsigned int j = 0; j <= pattern.getContent().size(); j++) {
    table[j][0] = j;
  }

  for(unsigned int i = 1; i<=text.getContent().size(); i++) {
    for(unsigned int j = 1; j<=pattern.getContent().size(); j++) {
      unsigned int value_a;
      if(pattern.getContent()[j-1] == text.getContent()[i-1]) {
        value_a = table[j-1][i-1];
      } else {
        value_a = table[j-1][i-1] + 1;
      }

      unsigned int value_b = UINT_MAX;
      if(j < pattern.getContent().size()) {
        value_b = table[j][i-1] + 1;
      }

      value_b = std::min(table[j-1][i] + 1, value_b);

      unsigned int value_c = UINT_MAX;
      if(j>1 && i>1 && pattern.getContent()[j-2] == text.getContent()[i-1] && pattern.getContent()[j-1] == text.getContent()[i-2]) {
        value_c = table[j-2][i-2] + 1;
      }

      table[j][i] = std::min({value_a, value_b, value_c});
    }
  }

  return table;
}

template <class SymbolType>
ext::set<unsigned int> GeneralizedLevenshteinDynamicProgramming::search(const string::LinearString<SymbolType> & text, const string::LinearString<SymbolType> & pattern, unsigned int errors) {
  auto table = GeneralizedLevenshteinDynamicProgramming::compute_table(text, pattern);

  ext::set<unsigned int> result;

  for(unsigned int i = 0; i<= text.getContent().size(); i++) {
    if(table[pattern.getContent().size()][i] <= errors) {
      result.insert(i);
    }
  }

  return result;
}


} // namespace simulations

} // namespace stringology

