Skip to content
Snippets Groups Projects
RegularEquationSolver.h 8.6 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*
     * RegularEquationSolver.h
     *
     *  Created on: 4. 3. 2014
    
    Jan Trávníček's avatar
    Jan Trávníček committed
     *	  Author: Tomas Pecka
    
    #ifndef REGULAR_EQUATIONS_SOLVER_H_
    #define REGULAR_EQUATIONS_SOLVER_H_
    
    #include <alib/algorithm>
    #include <alib/map>
    #include <alib/deque>
    
    #include <exception/CommonException.h>
    
    #include <regexp/unbounded/UnboundedRegExp.h>
    #include <regexp/unbounded/UnboundedRegExpElement.h>
    #include <regexp/unbounded/UnboundedRegExpElements.h>
    
    
    namespace equations {
    
    /**
     * Base class for regular equations solvers.
     */
    
    template < class TerminalSymbolType, class VariableSymbolType >
    
    Jan Trávníček's avatar
    Jan Trávníček committed
    class RegularEquationSolver {
    
    public:
    	/**
    	 * Adds nonterminal symbol into system.
    	 *
    	 * @param symb given symbol
    
    	void addVariableSymbol ( const VariableSymbolType & symbol );
    
    
    	/**
    	 * Removes nonterminal symbol from equation system.
    	 *
    	 * @param symb given symbol
    
    	 * @throws CommonException when symbol is in use
    
    	void removeVariableSymbol ( const VariableSymbolType & symbol );
    
    
    	/**
    	 * Sets nonterminal symbols of the equation system.
    	 * @param symbol Symbols to set
    
    	 * @throws CommonException
    
    	void setVariableSymbols ( const ext::set < VariableSymbolType > & symbols );
    
    
    	/**
    	 * Adds equation in form FROM = eq TO
    	 *
    	 * @param from symbol
    	 * @param to symbol
    	 * @param eq equation
    	 */
    
    	void addEquation ( const VariableSymbolType & from, const VariableSymbolType & to, const regexp::UnboundedRegExpElement < TerminalSymbolType > & eq );
    
    
    	/**
    	 * Adds equation in form: FROM = eq
    	 *
    	 * @param from
    	 * @param eq
    	 */
    
    	void addEquation ( const VariableSymbolType & from, const regexp::UnboundedRegExpElement < TerminalSymbolType > & eq );
    
    
    	/**
    	 * Solve expression system
    	 *
    	 * @param solveFor will solve equation system for given symbol
    	 * @return regexp
    	 */
    
    	regexp::UnboundedRegExp < TerminalSymbolType > solve ( const VariableSymbolType & solveFor );
    
    
    protected:
    	/**
    	 * actual equations elimination
    	 * @return pointer to solutions RegExp tree root
    	 */
    
    	virtual regexp::UnboundedRegExp < TerminalSymbolType > eliminate ( void ) = 0;
    
    	 * Runs BFS to determine depth of symbols in equation system and stores it in nonterminalSymbolsByDepth;
    	 * @see nonterminalSymbolsByDepth
    
    	void sortSymbolsByDepth ( const VariableSymbolType & solveFor );
    
    
    	/**
    	 * @see symbolsByDepth
    	 */
    
    	ext::deque < VariableSymbolType > nonterminalSymbolsByDepth;
    
    
    	/**
    	 * Stores transitions from nonterminal to nonterminal, eg A = 2A + 2B + 1C
    	 */
    
    	ext::map < std::pair < VariableSymbolType, VariableSymbolType >, regexp::UnboundedRegExpAlternation < TerminalSymbolType > > equationTransition;
    
    
    	/**
    	 * Stores equation not going to particular nonterminal, eg A = 01*
    	 */
    
    	ext::map < VariableSymbolType, regexp::UnboundedRegExpAlternation < TerminalSymbolType > > equationFinal;
    
    	ext::set < VariableSymbolType > nonterminalSymbols;
    
    template < class TerminalSymbolType, class VariableSymbolType >
    void RegularEquationSolver < TerminalSymbolType, VariableSymbolType >::setVariableSymbols ( const ext::set < VariableSymbolType > & newSymbols ) {
    	ext::set < VariableSymbolType > removed, added;
    
    	std::set_difference ( nonterminalSymbols.begin ( ), nonterminalSymbols.end ( ), newSymbols.begin ( ), newSymbols.end ( ), std::inserter ( removed, removed.end ( ) ) );
    	std::set_difference ( newSymbols.begin ( ), newSymbols.end ( ), nonterminalSymbols.begin ( ), nonterminalSymbols.end ( ), std::inserter ( added, added.end ( ) ) );
    
    	for ( const VariableSymbolType & symb : removed ) {
    		removeVariableSymbol ( symb );
    
    	for ( const VariableSymbolType & symb : added ) {
    		addVariableSymbol ( symb );
    
    template < class TerminalSymbolType, class VariableSymbolType >
    void RegularEquationSolver < TerminalSymbolType, VariableSymbolType >::removeVariableSymbol ( const VariableSymbolType & symb ) {
    	for ( const auto & kv : equationTransition ) {
    		const VariableSymbolType & from = kv.first.first;
    		const VariableSymbolType & to = kv.first.second;
    		const regexp::UnboundedRegExpAlternation < TerminalSymbolType > & alt = kv.second;
    
    		if ( ( from == symb || to == symb ) && alt.getElements ( ).size ( ) != 0 ) {
    
    			throw exception::CommonException ( "Symbol '" + ext::to_string ( symb ) + "' is in use." );
    
    	for ( const auto & kv : equationFinal ) {
    		const VariableSymbolType & from = kv.first;
    		const regexp::UnboundedRegExpAlternation < TerminalSymbolType > & alt = kv.second;
    
    		if ( from == symb && alt.getElements ( ).size ( ) != 0 ) {
    
    			throw exception::CommonException ( "Symbol '" + ext::to_string ( from ) + "' is in use." );
    
    	nonterminalSymbols.erase ( nonterminalSymbols.find ( symb ) );
    	equationFinal.erase ( equationFinal.find ( symb ) );
    
    	for ( const VariableSymbolType & s : nonterminalSymbols ) {
    		equationTransition.erase ( equationTransition.find ( std::make_pair ( s, symb ) ) );
    		equationTransition.erase ( equationTransition.find ( std::make_pair ( symb, s ) ) );
    
    
    	equationTransition.erase ( equationTransition.find ( std::make_pair ( symb, symb ) ) );
    
    template < class TerminalSymbolType, class VariableSymbolType >
    void RegularEquationSolver < TerminalSymbolType, VariableSymbolType >::addVariableSymbol ( const VariableSymbolType & symb ) {
    	for ( const VariableSymbolType & s : nonterminalSymbols) {
    		equationTransition.insert ( std::make_pair ( std::make_pair ( symb, s ), regexp::UnboundedRegExpAlternation < TerminalSymbolType > { } ) );
    		equationTransition.insert ( std::make_pair ( std::make_pair ( s, symb ), regexp::UnboundedRegExpAlternation < TerminalSymbolType > { } ) );
    
    	equationTransition.insert ( std::make_pair ( std::make_pair ( symb, symb ), regexp::UnboundedRegExpAlternation < TerminalSymbolType > { } ) );
    
    	nonterminalSymbols.insert ( symb );
    	equationFinal.insert ( std::make_pair ( symb, regexp::UnboundedRegExpAlternation < TerminalSymbolType > { } ) );
    
    template < class TerminalSymbolType, class VariableSymbolType >
    void RegularEquationSolver < TerminalSymbolType, VariableSymbolType >::addEquation ( const VariableSymbolType & from, const VariableSymbolType & to, const regexp::UnboundedRegExpElement < TerminalSymbolType > & eq ) {
    	if ( nonterminalSymbols.count ( from ) == 0 ) {
    
    		throw exception::CommonException ( "Symbol from ('" + ext::to_string ( from ) + "') is not in equation system." );
    
    	if ( nonterminalSymbols.count ( to ) == 0 ) {
    
    		throw exception::CommonException ( "Symbol to ('" + ext::to_string ( to ) + "') is not in equation system." );
    
    	equationTransition.find ( std::make_pair ( from, to ) )->second.appendElement ( eq );
    
    template < class TerminalSymbolType, class VariableSymbolType >
    void RegularEquationSolver < TerminalSymbolType, VariableSymbolType >::addEquation ( const VariableSymbolType & from, const regexp::UnboundedRegExpElement < TerminalSymbolType > & eq ) {
    	if ( nonterminalSymbols.count ( from ) == 0 ) {
    
    		throw exception::CommonException ( "Symbol from ('" + ext::to_string ( from ) + "') is not in equation system." );
    
    	equationFinal.find ( from )->second.appendElement ( eq );
    
    template < class TerminalSymbolType, class VariableSymbolType >
    void RegularEquationSolver < TerminalSymbolType, VariableSymbolType >::sortSymbolsByDepth ( const VariableSymbolType & solveFor ) {
    	ext::map < VariableSymbolType, bool > visited;
    	std::queue < VariableSymbolType > queue;
    
    	for(const VariableSymbolType & symbol : nonterminalSymbols ) {
    		visited [ symbol ] = false;
    
    	visited [ solveFor ] = true;
    	queue.push ( solveFor );
    
    	while ( ! queue.empty ( ) ) {
    		VariableSymbolType s = std::move ( queue.front ( ) );
    		queue.pop ( );
    
    		nonterminalSymbolsByDepth.push_back ( s );
    
    		for ( const auto & kv : equationTransition ) {
    
    			// find all transitions from current symbol that are non-empty, enqueue transition's target symbol
    
    			if ( kv.first.first == s && visited [ kv.first.second ] == false && kv.second.getElements ( ).size ( ) > 0 ) {
    				visited [ kv.first.second ] = true;
    				queue.push ( kv.first.second );
    
    template < class TerminalSymbolType, class VariableSymbolType >
    regexp::UnboundedRegExp < TerminalSymbolType > RegularEquationSolver < TerminalSymbolType, VariableSymbolType >::solve ( const VariableSymbolType & solveFor ) {
    	if ( nonterminalSymbols.count ( solveFor ) == 0 ) {
    
    		throw exception::CommonException ( "Symbol solveFor ('" + ext::to_string ( solveFor ) + "') is not in equation system." );
    
    	}
    
    	/*
    	 * Firstly, organize states by depth so we can output better looking
    	 * expressions. We need to solve equation system for automaton's initial state,
    	 * so lets start with the deepest ones and walk towards the initial one.
    	 */
    
    	sortSymbolsByDepth ( solveFor );
    	return eliminate ( );
    
    } /* namespace equations */
    
    
    #endif /* REGULAR_EQUATIONS_SOLVER_H_ */