Skip to content
Snippets Groups Projects
RegularEquationSolver.h 7.3 KiB
Newer Older
/*
 * 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 SymbolType >
Jan Trávníček's avatar
Jan Trávníček committed
class RegularEquationSolver {
public:
	/**
	 * Adds nonterminal symbol into system.
	 *
	 * @param symb given symbol
	void addSymbol(const SymbolType& symbol);

	/**
	 * Removes nonterminal symbol from equation system.
	 *
	 * @param symb given symbol
	 * @throws CommonException when symbol is in use
	void removeSymbol(const SymbolType& symbol);

	/**
	 * Sets nonterminal symbols of the equation system.
	 * @param symbol Symbols to set
	 * @throws CommonException
	void setSymbols(const ext::set<SymbolType>& symbols);

	/**
	 * Adds equation in form FROM = eq TO
	 *
	 * @param from symbol
	 * @param to symbol
	 * @param eq equation
	 */
	void addEquation(const SymbolType& from, const SymbolType& to, const regexp::UnboundedRegExpElement < SymbolType > & eq);

	/**
	 * Adds equation in form: FROM = eq
	 *
	 * @param from
	 * @param eq
	 */
	void addEquation(const SymbolType& from, const regexp::UnboundedRegExpElement < SymbolType > & eq);

	/**
	 * Solve expression system
	 *
	 * @param solveFor will solve equation system for given symbol
	 * @return regexp
	 */
	regexp::UnboundedRegExp < SymbolType > solve(const SymbolType& solveFor);

protected:
	/**
	 * actual equations elimination
	 * @return pointer to solutions RegExp tree root
	 */
	virtual regexp::UnboundedRegExp < SymbolType > eliminate(void) = 0;
	 * Runs BFS to determine depth of symbols in equation system and stores it in nonterminalSymbolsByDepth;
	 * @see nonterminalSymbolsByDepth
	void sortSymbolsByDepth(const SymbolType& solveFor);

	/**
	 * @see symbolsByDepth
	 */
	ext::deque<SymbolType> nonterminalSymbolsByDepth;

	/**
	 * Stores transitions from nonterminal to nonterminal, eg A = 2A + 2B + 1C
	 */
	ext::map<std::pair<SymbolType, SymbolType>, regexp::UnboundedRegExpAlternation < SymbolType > > equationTransition;

	/**
	 * Stores equation not going to particular nonterminal, eg A = 01*
	 */
	ext::map<SymbolType, regexp::UnboundedRegExpAlternation < SymbolType > > equationFinal;
	ext::set<SymbolType> nonterminalSymbols;
template < class SymbolType >
void RegularEquationSolver < SymbolType >::setSymbols(const ext::set<SymbolType>& newSymbols) {
	ext::set<SymbolType> 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 auto& symb : removed) {
		removeSymbol(symb);
	}

	for(const auto& symb : added) {
		addSymbol(symb);
	}
}

template < class SymbolType >
void RegularEquationSolver < SymbolType >::removeSymbol(const SymbolType& symb) {
	for(const auto& kv : equationTransition) {
		const SymbolType& from = kv.first.first;
		const SymbolType& to = kv.first.second;
		const regexp::UnboundedRegExpAlternation < SymbolType > & alt = kv.second;

		if((from == symb || to == symb) && alt.getElements().size() != 0) {
			throw exception::CommonException("Symbol '" + (std::string) symb + "' is in use.");
		}
	}

	for(const auto& kv : equationFinal) {
		const SymbolType& from = kv.first;
		const regexp::UnboundedRegExpAlternation < SymbolType > & alt = kv.second;

		if(from == symb && alt.getElements().size() != 0) {
			throw exception::CommonException("Symbol '" + (std::string) from + "' is in use.");
		}
	}


	nonterminalSymbols.erase(nonterminalSymbols.find(symb));
	equationFinal.erase(equationFinal.find(symb));

	for(const auto& 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 SymbolType >
void RegularEquationSolver < SymbolType >::addSymbol(const SymbolType& symb) {
	for(const auto& s : nonterminalSymbols) {
		equationTransition.insert(std::make_pair(std::make_pair(symb, s), regexp::UnboundedRegExpAlternation < SymbolType >  { }));
		equationTransition.insert(std::make_pair(std::make_pair(s, symb), regexp::UnboundedRegExpAlternation < SymbolType >  { }));
	}
	equationTransition.insert(std::make_pair(std::make_pair(symb, symb), regexp::UnboundedRegExpAlternation < SymbolType >  { }));

	nonterminalSymbols.insert(symb);
	equationFinal.insert(std::make_pair(symb, regexp::UnboundedRegExpAlternation < SymbolType >  { }));
}

template < class SymbolType >
void RegularEquationSolver < SymbolType >::addEquation(const SymbolType& from, const SymbolType& to, const regexp::UnboundedRegExpElement < SymbolType > & eq) {
	if(nonterminalSymbols.count(from) == 0) {
		throw exception::CommonException("Symbol from ('" + (std::string) from + "') is not in equation system.");
	}

	if(nonterminalSymbols.count(to) == 0) {
		throw exception::CommonException("Symbol to ('" + (std::string) to + "') is not in equation system.");
	}

	equationTransition.find(std::make_pair(from, to))->second.appendElement(eq);
}

template < class SymbolType >
void RegularEquationSolver < SymbolType >::addEquation(const SymbolType& from, const regexp::UnboundedRegExpElement < SymbolType > & eq) {
	if(nonterminalSymbols.count(from) == 0) {
		throw exception::CommonException("Symbol from ('" + (std::string) from + "') is not in equation system.");
	}

	equationFinal.find(from)->second.appendElement(eq);
}

template < class SymbolType >
void RegularEquationSolver < SymbolType >::sortSymbolsByDepth(const SymbolType& solveFor) {
	ext::map<SymbolType, bool> visited;
	std::queue<SymbolType> queue;

	for(const auto& symbol : nonterminalSymbols) {
		visited[symbol] = false;
	}

	visited[solveFor] = true;
	queue.push(solveFor);

	while(! queue.empty()) {
		SymbolType s = 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 SymbolType >
regexp::UnboundedRegExp < SymbolType > RegularEquationSolver < SymbolType >::solve(const SymbolType& solveFor) {
	if(nonterminalSymbols.count(solveFor) == 0) {
		throw exception::CommonException("Symbol solveFor ('" + (std::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_ */