/*
 * BadCharacterShiftTable.h
 *
 *  Created on: 5. 11. 2014
 *      Author: Jan Travnicek
 */

#pragma once

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

#include "LastVariableOffsetBack.h"

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

namespace tree {

namespace properties {

/**
 * Computation of BCS table for BMH from MI(E+\eps)-EVY course 2014
 * To get rid of zeros in BCS table we ignore last haystack character
 */
class BadCharacterShiftTable {
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 >
ext::map < common::ranked_symbol < SymbolType >, size_t > BadCharacterShiftTable::bcs ( const tree::PrefixRankedBarPattern < SymbolType > & pattern ) {
	return bcs ( tree::PrefixRankedBarNonlinearPattern < SymbolType > ( pattern ) );
}

template < class SymbolType >
ext::map < common::ranked_symbol < SymbolType >, size_t > BadCharacterShiftTable::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
	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 ( ) ) );
	}

	 // find the distance between the end of the pattern and the index
	 // of the last symbol representing the variable
	size_t lastSOffset = LastVariableOffsetBack::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 ) || symbol == pattern.getVariablesBar ( ) )
			continue;

		size_t tmp = lastSOffset;

		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 if ( tmp >= 2 )
			 // bar symbols match the variable bar which is one symbol after
			 // the last variable, conditioned because of the case S S| where
			 // the -1 would cause shift by 0 -- illegal
			tmp -= 1;

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

	 // limit the shift by position of symbols within the pattern
	for ( size_t i = 0; i < pattern.getContent ( ).size ( ) - 1; i++ ) { // last symbol is not concerned
		if ( pattern.getContent ( )[i] == pattern.getSubtreeWildcard ( ) || pattern.getNonlinearVariables ( ).count ( pattern.getContent ( )[i] ) || pattern.getContent ( )[i] == pattern.getVariablesBar ( ) )
			continue;

		size_t tmp = pattern.getContent ( ).size ( ) - i - 1;

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

	return bcs;
}

} /* namespace properties */

} /* namespace tree */

