#include <catch2/catch.hpp>

#include "string/LinearString.h"
#include "stringology/matching/OracleMatcherConstruction.h"
#include "stringology/query/BackwardOracleMatching.h"
#include "stringology/exact/ExactFactorMatch.h"

#include "string/generate/RandomStringFactory.h"
#include "string/generate/RandomSubstringFactory.h"

TEST_CASE ( "Oracle Matcher", "[unit][algo][stringology][query]" ) {
	SECTION ( "Test Backward Oracle Matching" ) {
		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) {
			string::LinearString < > subject ( subjects[i] );
			string::LinearString < > pattern ( patterns[i] );
			automaton::DFA < DefaultSymbolType, unsigned > oracleAutomaton = stringology::matching::OracleMatcherConstruction::construct ( pattern );
			ext::set < unsigned > res = stringology::query::BackwardOracleMatching::match ( subject, oracleAutomaton );
			INFO ( subjects[i] << ' ' << patterns[i] << ' ' << res );
			CHECK ( res == expectedOccs[i] );
		}

		auto longSubject = string::generate::RandomStringFactory::generateLinearString (64 * 64, 512, false, true);
		auto longPattern = string::generate::RandomSubstringFactory::generateSubstring(64 * 5, longSubject );
		automaton::DFA < std::string, unsigned > oracleAutomaton = stringology::matching::OracleMatcherConstruction::construct ( longPattern );
		ext::set < unsigned > res = stringology::query::BackwardOracleMatching::match ( longSubject, oracleAutomaton );
		ext::set < unsigned > resRef = stringology::exact::ExactFactorMatch::match ( longSubject, longPattern );
		INFO ( "long: " << res );
		CHECK ( res == resRef);
	}
}
