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

#ifndef _ARBOLOGY_REVERSED_QUICK_SEARCH_BAD_CHARACTER_SHIFT_TABLE_H_
#define _ARBOLOGY_REVERSED_QUICK_SEARCH_BAD_CHARACTER_SHIFT_TABLE_H_

#include <alib/set>
#include <alib/map>

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

#include "FirstVariableOffsetFront.h"

namespace tree {

namespace properties {

/**
 * BadCharacterShiftTable for the QuickSearch algorithm for tree pattern matching.
 */
class ReversedQuickSearchBadCharacterShiftTable {
public:
	template < class SymbolType >
	static ext::map < common::ranked_symbol < SymbolType >, size_t > bcs ( const tree::PrefixRankedBarPattern < SymbolType > & pattern );
	template < class SymbolType >
	static ext::map < common::ranked_symbol < SymbolType >, size_t > bcs ( const tree::PrefixRankedBarNonlinearPattern < SymbolType > & pattern );
	template < class SymbolType >
	static ext::map < common::ranked_symbol < SymbolType >, size_t > bcs ( const tree::PrefixRankedPattern < SymbolType > & pattern );
	template < class SymbolType >
	static ext::map < common::ranked_symbol < SymbolType >, size_t > bcs ( const tree::PrefixRankedNonlinearPattern < SymbolType > & pattern );

};

template < class SymbolType >
ext::map < common::ranked_symbol < SymbolType >, size_t > ReversedQuickSearchBadCharacterShiftTable::bcs ( const tree::PrefixRankedBarPattern < SymbolType > & pattern ) {
	return bcs ( tree::PrefixRankedBarNonlinearPattern < SymbolType > ( pattern ) );
}

template < class SymbolType >
ext::map < common::ranked_symbol < SymbolType >, size_t > ReversedQuickSearchBadCharacterShiftTable::bcs ( const tree::PrefixRankedBarNonlinearPattern < SymbolType > & pattern ) {
	const ext::set < common::ranked_symbol < SymbolType > > & alphabet = pattern.getAlphabet ( );

	ext::map < common::ranked_symbol < SymbolType >, size_t > bcs;

	// initialisation of bcs table to the size of the pattern plus one
	for ( const common::ranked_symbol < SymbolType > & symbol : alphabet ) {
		if ( ( symbol == pattern.getSubtreeWildcard ( ) ) || ( pattern.getNonlinearVariables ( ).count ( symbol ) ) || ( symbol == pattern.getVariablesBar ( ) ) ) continue;

		bcs.insert ( std::make_pair ( symbol, pattern.getContent ( ).size ( ) + 1 ) );
	}

	// find the distance between the beginning of the pattern and the index
	// of the first symbol representing the variable's bar
	size_t firstSBarOffset = FirstVariableOffsetFront::offset ( pattern ) + 1;

	// limit the shift by occurrence of the last variable
	for ( const common::ranked_symbol < SymbolType > & symbol : alphabet ) {
		if ( ( symbol == pattern.getSubtreeWildcard ( ) ) || ( pattern.getNonlinearVariables ( ).count ( symbol ) ) || ( symbol == pattern.getVariablesBar ( ) ) ) continue;

		size_t tmp = firstSBarOffset + 1;

		if ( pattern.getBars ( ).count ( symbol ) )
			// size of the smallest subtree containing given terminal depend
			// on the arity of the terminal
			tmp += ( size_t ) symbol.getRank ( ) * 2;
		else
			tmp -= 1;

		if ( bcs[symbol] > tmp )
			bcs[symbol] = tmp;
	}

	// limit the shift by position of symbols within the pattern
	for ( unsigned i = pattern.getContent ( ).size ( ); i > 0; i-- ) {
		if ( ( pattern.getContent ( )[i-1] == pattern.getSubtreeWildcard ( ) ) || ( pattern.getNonlinearVariables ( ).count ( pattern.getContent ( )[i-1] ) ) || ( pattern.getContent ( )[i-1] == pattern.getVariablesBar ( ) ) ) continue;

		size_t tmp = i;

		if ( bcs[pattern.getContent ( )[i-1]] > tmp )
			bcs[pattern.getContent ( )[i-1]] = tmp;
	}

	return bcs;
}

template < class SymbolType >
ext::map < common::ranked_symbol < SymbolType >, size_t > ReversedQuickSearchBadCharacterShiftTable::bcs ( const tree::PrefixRankedPattern < SymbolType > & pattern ) {
	return bcs ( tree::PrefixRankedNonlinearPattern < SymbolType > ( pattern ) );
}

template < class SymbolType >
ext::map < common::ranked_symbol < SymbolType >, size_t > ReversedQuickSearchBadCharacterShiftTable::bcs ( const tree::PrefixRankedNonlinearPattern < SymbolType > & pattern ) {
	const ext::set < common::ranked_symbol < SymbolType > > & alphabet = pattern.getAlphabet ( );

	ext::map < common::ranked_symbol < SymbolType >, size_t > bcs;

	// initialisation of bcs table to the size of the pattern plus one
	for ( const common::ranked_symbol < SymbolType > & symbol : alphabet ) {
		if ( symbol == pattern.getSubtreeWildcard ( ) || pattern.getNonlinearVariables ( ).count ( symbol ) ) continue;

		bcs.insert ( std::make_pair ( symbol, pattern.getContent ( ).size ( ) + 1 ) );
	}

	// find the distance between the beginning of the pattern and the index
	// of the first symbol representing the variable's bar
	size_t firstSOffset = FirstVariableOffsetFront::offset ( pattern );

	// limit the shift by occurrence of the last variable
	for ( const common::ranked_symbol < SymbolType > & symbol : alphabet ) {
		if ( symbol == pattern.getSubtreeWildcard ( ) || pattern.getNonlinearVariables ( ).count ( symbol ) ) continue;

		if ( bcs[symbol] > firstSOffset + 1 )
			bcs[symbol] = firstSOffset + 1;
	}

	// limit the shift by position of symbols within the pattern
	for ( unsigned i = pattern.getContent ( ).size ( ) - 1; i > 0; i-- ) {
		if ( pattern.getContent ( )[i-1] == pattern.getSubtreeWildcard ( ) || pattern.getNonlinearVariables ( ).count ( pattern.getContent ( )[i-1] ) ) continue;
		size_t tmp = i;

		if ( bcs[pattern.getContent ( )[i-1]] > tmp )
			bcs[pattern.getContent ( )[i-1]] = tmp;
	}

	return bcs;
}

} /* namespace properties */

} /* namespace tree */

#endif /* _ARBOLOGY_REVERSED_QUICK_SEARCH_BAD_CHARACTER_SHIFT_TABLE_H_ */
