/* * RegularEquationSolver.h * * Created on: 4. 3. 2014 * Author: Tomas Pecka */ #ifndef REGULAR_EQUATIONS_SOLVER_H_ #define REGULAR_EQUATIONS_SOLVER_H_ #include <alib/algorithm> #include <alib/map> #include <alib/deque> #include <queue> #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 > 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; /** * Set of symbols */ 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 ( ).empty ( ) ) { 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 ( ).empty ( ) ) { 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 ( ).empty ( ) ) { 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_ */