/*
 *  GeneralizedLevenshteinBitParalelism.h
 *
 *  Created on: 29.4.2018
 *      Author: Tomas Capek
 */

#ifndef _GENERALIZED_LEVENSHTEIN_BIT_PARALELISM_H__
#define _GENERALIZED_LEVENSHTEIN_BIT_PARALELISM_H__

#include <exception>
#include <string/LinearString.h>

#include "BitParalelism.h"

namespace stringology {

namespace simulations {

class GeneralizedLevenshteinBitParalelism {
public:
    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::set<unsigned int> GeneralizedLevenshteinBitParalelism::search(const string::LinearString<SymbolType> & text, const string::LinearString<SymbolType> & pattern, unsigned int errors) {
  // preparation stage
  ext::set<SymbolType> common_alphabet = text.getAlphabet();
  common_alphabet.insert(pattern.getAlphabet().begin(), pattern.getAlphabet().end());

  ext::map<SymbolType, ext::vector<bool> > D_vectors = BitParalelism::constructDVectors(common_alphabet, pattern);

  auto V_vector = ext::vector<bool>(pattern.getContent().size(), 0);
  V_vector[pattern.getContent().size() - 1] = 1;

  // computation part
  ext::set<unsigned int> result;

  ext::vector<ext::vector<bool> > B_vectors;
  for(unsigned int i=0; i<=errors; i++) {
    B_vectors.push_back(ext::vector<bool>(pattern.getContent().size(), 0));
  }

  for(unsigned int l = 0; l <= errors; l++) {
    for(unsigned int j = l; j <= pattern.getContent().size(); j++) {
      B_vectors[l][j] = 1;
    }
  }

  ext::vector<ext::vector<bool> > S_vectors;
  for(unsigned int i=0; i<=errors; i++) {
    S_vectors.push_back(ext::vector<bool>(pattern.getContent().size(), 1));
  }

  // 0-th column R-vectors
  ext::vector<bool> b0 = ext::vector < bool > ( pattern.getContent().size(), true );
  for(unsigned int j=1; j<=errors; j++) {
	b0 <<= 1;
	if(b0[pattern.getContent().size()-1] == false) {
        result.insert(0);
        break;
	}
  }

  for(unsigned int i=0; i<text.getContent().size(); i++) {
    ext::vector< ext::vector<bool> > previous_B_vectors = B_vectors;
    ext::vector< ext::vector<bool> > previous_S_vectors = S_vectors;

    auto D_vector = D_vectors[text.getContent()[i]];

    B_vectors[0] = (previous_B_vectors[0] << 1) | D_vector;
    S_vectors[0] = (previous_B_vectors[0] << 1) | (D_vector >> 1);

    for(unsigned int j=1; j<=errors; j++) {
      B_vectors[j] = ((previous_B_vectors[j] << 1) | D_vector) &
                     ((previous_B_vectors[j-1] & B_vectors[j-1] & (previous_S_vectors[j-1] | D_vector)) << 1) &
                     (previous_B_vectors[j-1] | V_vector);

      S_vectors[j] = (previous_B_vectors[j] << 1) | (D_vector >> 1);
    }

    for (const auto & data : B_vectors) {
      if(data[pattern.getContent().size()-1] == false) {
        result.insert(i+1);
        break;
      }
    }
  }

  return result;
}

} // namespace simulations

} // namespace stringology

#endif /* _GENERALIZED_LEVENSHTEIN_BIT_PARALELISM_H__ */
