/*
 * ReversedQuickSearch.h
 *
 *  Created on: 21. 3. 2018
 *	  Author: Michal Cvach
 */

#pragma once

#include <alib/set>
#include <alib/map>
#include <common/ranked_symbol.hpp>

#include <tree/properties/ReversedQuickSearchBadCharacterShiftTable.h>
#include <tree/properties/SubtreeJumpTable.h>
#include <tree/properties/ExactSubtreeRepeatsNaive.h>
#include <tree/exact/ForwardOccurrenceTest.h>

#include <tree/ranked/PrefixRankedTree.h>
#include <tree/ranked/PrefixRankedBarTree.h>
#include <tree/ranked/PrefixRankedPattern.h>
#include <tree/ranked/PrefixRankedBarPattern.h>
#include <tree/ranked/PrefixRankedNonlinearPattern.h>
#include <tree/ranked/PrefixRankedBarNonlinearPattern.h>

namespace arbology {

namespace exact {

/**
 * Implementation of the Reversed Quick Search algorithm for tree pattern matching.
 * This variant searches the subject tree from right to left, while comparing matches from left to right.
 * This algorithm makes use of a Bad character shift table as well as a Subtree jump table.
 */
class ReversedQuickSearch {
public:
	/**
	 * Search for a tree pattern in a tree.
	 * @return set set of occurences
	 */
	template < class SymbolType >
	static ext::set < unsigned > match ( const tree::PrefixRankedBarTree < SymbolType > & subject, const tree::PrefixRankedBarTree < SymbolType > & pattern );
	template < class SymbolType >
	static ext::set < unsigned > match ( const tree::PrefixRankedBarTree < SymbolType > & subject, const tree::PrefixRankedBarPattern < SymbolType > & pattern );
	template < class SymbolType >
	static ext::set < unsigned > match ( const tree::PrefixRankedBarTree < SymbolType > & subject, const tree::PrefixRankedBarNonlinearPattern < SymbolType > & pattern );
	template < class SymbolType >
	static ext::set < unsigned > match ( const tree::PrefixRankedTree < SymbolType > & subject, const tree::PrefixRankedTree < SymbolType > & pattern );
	template < class SymbolType >
	static ext::set < unsigned > match ( const tree::PrefixRankedTree < SymbolType > & subject, const tree::PrefixRankedPattern < SymbolType > & pattern );
	template < class SymbolType >
	static ext::set < unsigned > match ( const tree::PrefixRankedTree < SymbolType > & subject, const tree::PrefixRankedNonlinearPattern < SymbolType > & pattern );

};

template < class SymbolType >
ext::set < unsigned > ReversedQuickSearch::match ( const tree::PrefixRankedBarTree < SymbolType > & subject, const tree::PrefixRankedBarTree < SymbolType > & pattern ) {
	return match ( subject, tree::PrefixRankedBarPattern < SymbolType > ( pattern ) );
}

template < class SymbolType >
ext::set < unsigned > ReversedQuickSearch::match ( const tree::PrefixRankedBarTree < SymbolType > & subject, const tree::PrefixRankedBarPattern < SymbolType > & pattern ) {
	ext::set < unsigned > occ;
	ext::map < common::ranked_symbol < SymbolType >, size_t > bcs = tree::properties::ReversedQuickSearchBadCharacterShiftTable::bcs ( pattern ); // NOTE: the subjects alphabet must be a subset or equal to the pattern
	ext::vector < int > subjectSubtreeJumpTable = tree::properties::SubtreeJumpTable::compute ( subject );

	// index to the subject
	int i = ( int ) subject.getContent ( ).size ( ) - pattern.getContent ( ).size ( );

	// main loop of the algorithm over all possible indexes where the pattern can start
	while ( i >= 0 ) {

		 // index to the pattern
		unsigned j = tree::exact::ForwardOccurrenceTest::occurrence ( subject, subjectSubtreeJumpTable, pattern, i );

		// match was found
		if ( j == pattern.getContent ( ).size ( ) ) occ.insert ( i );

		// we have to break if the pattern is already aligned at the leftmost position
		// otherwise we would ask for symbol at the position -1 in the shift heuristics
		if ( i == 0 ) {
			break;
		}

		// shift heuristics
		i -= bcs[subject.getContent ( )[i-1]];
	}

	return occ;
}

template < class SymbolType >
ext::set < unsigned > ReversedQuickSearch::match ( const tree::PrefixRankedBarTree < SymbolType > & subject, const tree::PrefixRankedBarNonlinearPattern < SymbolType > & pattern ) {
	ext::set < unsigned > occ;
	ext::map < common::ranked_symbol < SymbolType >, size_t > bcs = tree::properties::ReversedQuickSearchBadCharacterShiftTable::bcs ( pattern ); // NOTE: the subjects alphabet must be a subset or equal to the pattern
	ext::vector < int > subjectSubtreeJumpTable = tree::properties::SubtreeJumpTable::compute ( subject );
	ext::map < common::ranked_symbol < SymbolType >, unsigned > variablesSetting;

	tree::PrefixRankedBarTree < unsigned > repeats = tree::properties::ExactSubtreeRepeatsNaive::repeats ( subject );

	// index to the subject
	int i = ( int ) subject.getContent ( ).size ( ) - pattern.getContent ( ).size ( );

	// main loop of the algorithm over all possible indexes where the pattern can start
	while ( i >= 0 ) {
		// index to the pattern
		unsigned j = tree::exact::ForwardOccurrenceTest::occurrence ( subject, subjectSubtreeJumpTable, repeats, pattern, i );

		// match was found
		if ( j == pattern.getContent ( ).size ( ) ) occ.insert ( i );

		// we have to break if the pattern is already aligned at the leftmost position
		// otherwise we would ask for symbol at the position -1 in the shift heuristics
		if ( i == 0 ) {
			break;
		}

		// shift heuristics
		i -= bcs[subject.getContent ( )[i-1]];
	}

	return occ;
}

template < class SymbolType >
ext::set < unsigned > ReversedQuickSearch::match ( const tree::PrefixRankedTree < SymbolType > & subject, const tree::PrefixRankedTree < SymbolType > & pattern ) {
	return match ( subject, tree::PrefixRankedPattern < SymbolType > ( pattern ) );
}

template < class SymbolType >
ext::set < unsigned > ReversedQuickSearch::match ( const tree::PrefixRankedTree < SymbolType > & subject, const tree::PrefixRankedPattern < SymbolType > & pattern ) {
	ext::set < unsigned > occ;
	ext::map < common::ranked_symbol < SymbolType >, size_t > bcs = tree::properties::ReversedQuickSearchBadCharacterShiftTable::bcs ( pattern ); // NOTE: the subjects alphabet must be a subset or equal to the pattern
	ext::vector < int > subjectSubtreeJumpTable = tree::properties::SubtreeJumpTable::compute ( subject );

	// index to the subject
	int i = ( int ) subject.getContent ( ).size ( ) - pattern.getContent ( ).size ( );

	// main loop of the algorithm over all possible indexes where the pattern can start
	while ( i >= 0 ) {
		// index to the pattern
		unsigned j = tree::exact::ForwardOccurrenceTest::occurrence ( subject, subjectSubtreeJumpTable, pattern, i );

		// match was found
		if ( j == pattern.getContent ( ).size ( ) ) occ.insert ( i );

		// we have to break if the pattern is already aligned at the leftmost position
		// otherwise we would ask for symbol at the position -1 in the shift heuristics
		if ( i == 0 ) {
			break;
		}

		// shift heuristics
		i -= bcs[subject.getContent ( )[i-1]];
	}

	return occ;
}

template < class SymbolType >
ext::set < unsigned > ReversedQuickSearch::match ( const tree::PrefixRankedTree < SymbolType > & subject, const tree::PrefixRankedNonlinearPattern < SymbolType > & pattern ) {
	ext::set < unsigned > occ;
	ext::map < common::ranked_symbol < SymbolType >, size_t > bcs = tree::properties::ReversedQuickSearchBadCharacterShiftTable::bcs ( pattern ); // NOTE: the subjects alphabet must be a subset or equal to the pattern
	ext::vector < int > subjectSubtreeJumpTable = tree::properties::SubtreeJumpTable::compute ( subject );
	tree::PrefixRankedTree < unsigned > repeats = tree::properties::ExactSubtreeRepeatsNaive::repeats ( subject );

	// index to the subject
	int i = ( int ) subject.getContent ( ).size ( ) - pattern.getContent ( ).size ( );

	// main loop of the algorithm over all possible indexes where the pattern can start
	while ( i >= 0 ) {
		// index to the pattern
		unsigned j = tree::exact::ForwardOccurrenceTest::occurrence ( subject, subjectSubtreeJumpTable, repeats, pattern, i );

		// match was found
		if ( j == pattern.getContent ( ).size ( ) ) occ.insert ( i );

		// we have to break if the pattern is already aligned at the leftmost position
		// otherwise we would ask for symbol at the position -1 in the shift heuristics
		if ( i == 0 ) {
			break;
		}

		// shift heuristics
		i -= bcs[subject.getContent ( )[i-1]];
	}

	return occ;
}

} /* namespace exact */

} /* namespace arbology */

