#include <catch2/catch.hpp>

#include <string/String.h>
#include <stringology/matching/WideBNDMMatcherConstruction.h>
#include <stringology/query/WideBNDMOccurrences.h>
#include <stringology/matching/BNDMMatcherConstruction.h>
#include <stringology/query/BNDMOccurrences.h>
#include <stringology/exact/ExactFactorMatch.h>

#include <string/generate/RandomStringFactory.h>
#include <string/generate/RandomSubstringFactory.h>

TEST_CASE ( "Backward Nondeterministic DAWG", "[unit][algo][stringology][matching]" ) {
	SECTION ( "Simple" ) {
		ext::vector<std::string> subjects;
		ext::vector<std::string> patterns;
		ext::vector<ext::set<unsigned>> expectedOccs;

		subjects.push_back("a"); patterns.push_back("a"); expectedOccs.push_back({0});
		subjects.push_back("a"); patterns.push_back("b"); expectedOccs.push_back({});
		subjects.push_back("alfalfalfa"); patterns.push_back("alfalfalfa"); expectedOccs.push_back({0});
		subjects.push_back("alfalfalfa"); patterns.push_back("blfalfalfa"); expectedOccs.push_back({});
		subjects.push_back("alfalfalfa"); patterns.push_back("alfalfalfb"); expectedOccs.push_back({});
		subjects.push_back("alfalfalfaalfalfalfaalfalfalfaalfalfalfaalfalfalfaalfalfalfaalfa"); patterns.push_back("alfalfalfaalfalfalfaalfalfalfaalfalfalfaalfalfalfaalfalfalfaalfa"); expectedOccs.push_back({0});
		subjects.push_back("alfalfalfaalfalfalfaabfalfalfaalfalfalfaalfalfalfaalfalfalfaalfa"); patterns.push_back("alfalfalfaalfalfalfaalfalfalfaalfalfalfaalfalfalfaalfalfalfaalfa"); expectedOccs.push_back({});
		subjects.push_back("alfalfalfaalfalfalfaalfalfalfaalfalfalfaalfalfalfaalfalfalfaalfaa"); patterns.push_back("alfalfalfaalfalfalfaalfalfalfaalfalfalfaalfalfalfaalfalfalfaalfaa"); expectedOccs.push_back({0});
		subjects.push_back("atggccttgcc"); patterns.push_back("gcc"); expectedOccs.push_back({3,8});
		subjects.push_back("aaaaaaaaaa"); patterns.push_back("a"); expectedOccs.push_back({0,1,2,3,4,5,6,7,8,9});

		for(size_t i = 0; i < subjects.size(); ++i) {
			indexes::stringology::BitParallelIndex < char > bndmPattern1 = stringology::matching::WideBNDMMatcherConstruction::construct ( string::LinearString < char > ( patterns[i] ) );
			indexes::stringology::BitSetIndex < char > bndmPattern2 = stringology::matching::BNDMMatcherConstruction::construct ( string::LinearString < char > ( patterns[i] ) );
			ext::set < unsigned > res1 = stringology::query::WideBNDMOccurrences::query ( bndmPattern1, string::LinearString < char > ( subjects[i] ) );
			ext::set < unsigned > res2 = stringology::query::BNDMOccurrences::query ( bndmPattern2, string::LinearString < char > ( subjects[i] ) );
			CHECK ( res1 == expectedOccs[i] );
			CHECK ( res2 == expectedOccs[i] );
			INFO ( subjects[i] << ' ' << patterns[i] << ' ' << res1 );
		}

		auto longSubject = string::generate::RandomStringFactory::generateLinearString (64 * 64 * 64, 512, false, true);
		auto longPattern = string::generate::RandomSubstringFactory::generateSubstring(64 * 32 * 32, longSubject );
		indexes::stringology::BitSetIndex < std::string > pattern = stringology::matching::BNDMMatcherConstruction::construct ( longPattern );
		ext::set < unsigned > res = stringology::query::BNDMOccurrences::query ( pattern, longSubject );
		ext::set < unsigned > ref = stringology::exact::ExactFactorMatch::match ( longSubject, longPattern );
		INFO ( "long: " << res );
		CHECK ( res == ref);
	}
}
