Skip to content
Snippets Groups Projects
AlgorithmRegistry.cpp 9.83 KiB
Newer Older
/*
 * AlgorithmRegistry.cpp
 *
 *  Created on: 19. 8. 2017
 *	  Author: Jan Travnicek
 */

#include <registry/AlgorithmRegistry.hpp>
#include <registry/CastRegistry.hpp>
#include <alib/foreach>
#include <alib/typeinfo>

namespace abstraction {

ext::map < ext::pair < std::string, ext::vector < std::string > >, ext::list < std::shared_ptr < AlgorithmRegistry::Entry > > > & AlgorithmRegistry::getEntries ( ) {
	static ext::map < ext::pair < std::string, ext::vector < std::string > >, ext::list < std::shared_ptr < Entry > > > algorithmGroups;
bool AlgorithmRegistry::isRegistered ( const std::string & algorithm, const ext::vector < std::string > & templateParams, AlgorithmCategories::AlgorithmCategory category, const ext::vector < ext::tuple < std::string, ext::set < abstraction::ParamQualifiers::ParamQualifier >, std::string > > & params ) {
	auto & group = getEntries ( ) [ ext::make_pair ( algorithm, templateParams ) ];
	for ( const std::shared_ptr < Entry > & entry : group )
		if ( entry->getCategory ( ) == category && entry->getParams ( ) == params ) // TODO parameter names should not be used in comparison
			return true;

	return false;
}

void AlgorithmRegistry::registerInternal ( std::string algorithm, ext::vector < std::string > templateParams, std::shared_ptr < Entry > value ) {
	auto & group = getEntries ( ) [ ext::make_pair ( std::move ( algorithm ), std::move ( templateParams ) ) ];
	group.insert ( group.end ( ), value );
}

void AlgorithmRegistry::unregisterInternal ( std::string algorithm, ext::vector < std::string > templateParams, AlgorithmCategories::AlgorithmCategory category, ext::vector < ext::tuple < std::string, ext::set < abstraction::ParamQualifiers::ParamQualifier >, std::string > > params ) {
	auto & group = getEntries ( ) [ ext::make_pair ( algorithm, templateParams ) ];
	auto iter = find_if ( group.begin ( ), group.end ( ), [ & ] ( const std::shared_ptr < Entry > & entry ) {
				if ( entry->getCategory ( ) != category )
				const ext::vector < ext::tuple < std::string, ext::set < abstraction::ParamQualifiers::ParamQualifier >, std::string > > & entryParams = entry->getParams ( );
				if ( entryParams.size ( ) != params.size ( ) )
					return false;

				for ( unsigned i = 0; i < params.size ( ); ++ i ) {
					if ( std::get < 0 > ( params [ i ] ) != std::get < 0 > ( entryParams [ i ] ) )
						return false;
					if ( std::get < 1 > ( params [ i ] ) != std::get < 1 > ( entryParams [ i ] ) )
						return false;
				}

				return true;
			} );
	if ( iter == group.end ( ) ) {
		if ( templateParams.size ( ) == 0 )
			throw std::invalid_argument ( "Entry " + algorithm + " with parameters " + ext::to_string ( params ) + " not registered." );
		else
			throw std::invalid_argument ( "Templated entry " + algorithm + " < " + ext::to_string ( templateParams ) + " > with parameters " + ext::to_string ( params ) + " not registered." );
	}
	group.erase ( iter );
	if ( group.size ( ) == 0 )
		getEntries ( ).erase ( ext::make_pair ( algorithm, templateParams ) );
ext::map < ext::pair < std::string, ext::vector < std::string > >, ext::list < std::shared_ptr < AlgorithmRegistry::Entry > > >::const_iterator AlgorithmRegistry::findAbstractionGroup ( const std::string & name, const ext::vector < std::string > & templateParams ) {
	auto group = getEntries ( ).find ( ext::make_pair ( name, templateParams ) );
	if ( group == getEntries ( ).end ( ) ) {
		for ( auto iter = getEntries ( ).begin ( ); iter != getEntries ( ).end ( ); ++ iter ) {
			if ( ext::is_same_type ( name, iter->first.first ) && ext::are_same_types ( templateParams, iter->first.second ) ) {
				if ( group == getEntries ( ).end ( ) )
					group = iter;
				else if ( templateParams.size ( ) == 0 )
					throw std::invalid_argument ( "Name " + name + " is ambigous " );
				else
					throw std::invalid_argument ( "Templated name " + name + " < " + ext::to_string ( templateParams ) + " > is ambigous " );
	if ( group == getEntries ( ).end ( ) ) {
		if ( templateParams.size ( ) == 0 )
			throw std::invalid_argument ( "Entry " + name + " not available" );
		else
			throw std::invalid_argument ( "Templated entry " + name + " < " + ext::to_string ( templateParams ) + " > not available" );
	}
	return group;
}

std::shared_ptr < abstraction::OperationAbstraction > AlgorithmRegistry::getAbstraction ( const std::string & name, const ext::vector < std::string > & templateParams, const ext::vector < std::string > & paramTypes, AlgorithmCategories::AlgorithmCategory ) {
	auto group = findAbstractionGroup ( name, templateParams );
	auto incompatibleLambda = [ ] ( MatchType compatibility ) {
		return compatibility == MatchType::INCOMPATIBLE;
	};

	// determine how one can actually map what we have ( paramTypes ) as params to what is available as overloads ( group->second )
	std::vector < std::pair < ext::vector < MatchType >, std::shared_ptr < Entry > > > compatibilityData;
	for ( const std::shared_ptr < Entry > & entry : group->second ) {
		if ( entry->getParams ( ).size ( ) != paramTypes.size ( ) )
		ext::vector < MatchType > compatibilityVector;
		for ( unsigned i = 0; i < paramTypes.size ( ); ++ i ) {
			MatchType matchType;
			if ( std::get < 0 > ( entry->getParams ( ) [ i ] ) == paramTypes [ i ] ) {
				matchType = MatchType::EXACT;
			} else if ( abstraction::CastRegistry::castAvailable ( std::get < 0 > ( entry->getParams ( ) [ i ] ), paramTypes [ i ] ) ) {
				matchType = MatchType::CAST;
			} else {
				matchType = MatchType::INCOMPATIBLE;
			}

			compatibilityVector.push_back ( matchType );
		}

		// clear incompatibilities are fitered out
		if ( std::none_of ( compatibilityVector.begin ( ), compatibilityVector.end ( ), incompatibleLambda ) )
			compatibilityData.push_back ( std::make_pair ( std::move ( compatibilityVector ), entry ) );
	}

	// remaining compatible overloads are examined per parameter and the best option is remembered as overload index that achieved it
	ext::vector < ext::set < unsigned > > winnerList;
	for ( unsigned i = 0; i < paramTypes.size ( ); ++ i ) {
		ext::set < unsigned > best;

		unsigned overload = 0;
		for ( const std::pair < ext::vector < MatchType >, std::shared_ptr < Entry > > & data : compatibilityData ) {
			if ( data.first [ i ] == MatchType::EXACT )
				best.insert ( overload );

			++ overload;
		}

		if ( best.size ( ) > 0 ) {
			winnerList.push_back ( std::move ( best ) );
			continue;
		}

		overload = 0;
		for ( const std::pair < ext::vector < MatchType >, std::shared_ptr < Entry > > & data : compatibilityData ) {
			if ( data.first [ i ] == MatchType::CAST )
				best.insert ( overload );

			++ overload;
		}

		winnerList.push_back ( std::move ( best ) );
	}

	// intersection of everything together finds indexes which are better or of the same quality for all params over all overloads
	ext::set < unsigned > winner { ext::sequence < unsigned > ( 0 ).begin ( ), ext::sequence < unsigned > ( compatibilityData.size ( ) ).end ( ) };
Jan Trávníček's avatar
Jan Trávníček committed
	for ( const ext::set < unsigned > & best : winnerList ) {
		ext::set < unsigned > filtered;
		std::set_intersection ( winner.begin ( ), winner.end ( ), best.begin ( ), best.end ( ), std::inserter ( filtered, filtered.end ( ) ) );
		winner = std::move ( filtered );
	}

	// if there is a sinle winner, return it
	std::shared_ptr < Entry > best;
	if ( winner.size ( ) == 1 ) {
		best = compatibilityData [ * winner.begin ( ) ].second;
	} else if ( winner.size ( ) > 1 ) {
//		throw std::invalid_argument ( "Entry overload " + ext::to_string ( paramTypes ) + " ambiguous." ); FIXME make better overload select algorithm
		best = compatibilityData [ * winner.begin ( ) ].second;
/*		if(common::GlobalData::verbose)
			common::Streams::err << "Entry overload " + ss.str ( ) + " ambiguous. First selected." << std::endl;*/
		if ( templateParams.size ( ) == 0 )
			throw std::invalid_argument ( "Entry overload " + name + " " + ext::to_string ( paramTypes ) + " not available" );
		else
			throw std::invalid_argument ( "Templated entry overload " + name + " < " + ext::to_string ( templateParams ) + " > " + ext::to_string ( paramTypes ) + " not available" );
	}

	return best->getAbstraction ( );
}

ext::set < ext::pair < std::string, ext::vector < std::string > > > AlgorithmRegistry::listGroup ( const std::string & group ) {
	ext::set < ext::pair < std::string, ext::vector < std::string > > > res;
	for ( const std::pair < const ext::pair < std::string, ext::vector < std::string > >, ext::list < std::shared_ptr < Entry > > > & entry : getEntries ( ) )
		if ( entry.first.first.find ( group ) == 0 ) //found at the begining
			res.insert ( entry.first );

	return res;
ext::set < ext::tuple < AlgorithmCategories::AlgorithmCategory, ext::pair < std::string, ext::set < abstraction::ParamQualifiers::ParamQualifier > >, ext::vector < ext::tuple < std::string, ext::set < abstraction::ParamQualifiers::ParamQualifier >, std::string > > > > AlgorithmRegistry::listOverloads ( const std::string & algorithm, const ext::vector < std::string > & templateParams ) {
	auto group = findAbstractionGroup ( algorithm, templateParams );
	ext::set < ext::tuple < AlgorithmCategories::AlgorithmCategory, ext::pair < std::string, ext::set < abstraction::ParamQualifiers::ParamQualifier > >, ext::vector < ext::tuple < std::string, ext::set < abstraction::ParamQualifiers::ParamQualifier >, std::string > > > > res;
	for ( const std::shared_ptr < Entry > & overloads : group->second )
		res.insert ( ext::make_tuple ( overloads->getCategory ( ), overloads->getResult ( ), overloads->getParams ( ) ) );
ext::set < ext::pair < std::string, ext::vector < std::string > > > AlgorithmRegistry::list ( ) {
	ext::set < ext::pair < std::string, ext::vector < std::string > > > res;
	for ( const std::pair < const ext::pair < std::string, ext::vector < std::string > >, ext::list < std::shared_ptr < Entry > > > & entry : getEntries ( ) )
		res.insert ( entry.first );
}

} /* namespace abstraction */