From 9c4a6b19164ece7a36251cb71141165d3bfdeb83 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Pecka?= <peckato1@fit.cvut.cz>
Date: Thu, 11 Sep 2014 16:40:26 +0200
Subject: [PATCH] algo: StateElimination

---
 aconversions2/src/ConversionHandler.cpp       |  58 +++-
 aconversions2/src/ConversionHandler.h         |   7 +-
 .../fa2re/formal/StateEliminationFormal.cpp   | 292 ++++++++++++++++++
 .../fa2re/formal/StateEliminationFormal.h     |  62 ++++
 .../unbounded/StateEliminationUnbounded.cpp   | 272 ++++++++++++++++
 .../unbounded/StateEliminationUnbounded.h     |  62 ++++
 tests.aconversion.sh                          |  28 +-
 7 files changed, 755 insertions(+), 26 deletions(-)
 create mode 100644 alib2algo/src/conversions/fa2re/formal/StateEliminationFormal.cpp
 create mode 100644 alib2algo/src/conversions/fa2re/formal/StateEliminationFormal.h
 create mode 100644 alib2algo/src/conversions/fa2re/unbounded/StateEliminationUnbounded.cpp
 create mode 100644 alib2algo/src/conversions/fa2re/unbounded/StateEliminationUnbounded.h

diff --git a/aconversions2/src/ConversionHandler.cpp b/aconversions2/src/ConversionHandler.cpp
index a3c7a957a7..f7367ae991 100644
--- a/aconversions2/src/ConversionHandler.cpp
+++ b/aconversions2/src/ConversionHandler.cpp
@@ -7,7 +7,8 @@
 
 #include "ConversionHandler.h"
 
-//#include "conversions/fa2re/StateElimination.h"
+#include "conversions/fa2re/unbounded/StateEliminationUnbounded.h"
+#include "conversions/fa2re/formal/StateEliminationFormal.h"
 #include "conversions/fa2re/BrzozowskiAlgebraic.h"
 
 #include "conversions/re2fa/Glushkov.h"
@@ -54,7 +55,7 @@ void ConversionHandler::convert( void )
 {
 	if( m_source == FINITE_AUTOMATON )
 		convertFSM( );
-	else if( m_source == REGULAR_EXPRESSION )
+	else if( isRegExp(m_source) )
 		convertRE( );
 	else if( isGrammar( m_source ) )
 		convertRG( );
@@ -68,7 +69,7 @@ void ConversionHandler::convertFSM( void )
 {
 	if( m_target == FINITE_AUTOMATON )
 		throw exception::AlibException( "ConversionHandler: No valid conversion from FSM to FSM." );
-	else if( m_target == REGULAR_EXPRESSION )
+	else if( isRegExp( m_target ) )
 		convertFSMtoRE( );
 	else if( isGrammar( m_target ) )
 		convertFSMtoRG( );
@@ -80,7 +81,7 @@ void ConversionHandler::convertRE( void )
 {
 	if( m_target == FINITE_AUTOMATON )
 		convertREtoFSM( );
-	else if( m_target == REGULAR_EXPRESSION )
+	else if( isRegExp( m_target ) )
 		throw exception::AlibException( "ConversionHandler: No valid conversion from RE to RE." );
 	else if( isGrammar( m_target ) )
 		convertREtoRG( );
@@ -92,7 +93,7 @@ void ConversionHandler::convertRG( void )
 {
 	if( m_target == FINITE_AUTOMATON )
 		convertRGtoFSM( );
-	else if( m_target == REGULAR_EXPRESSION )
+	else if( isRegExp( m_target ) )
 		convertRGtoRE( );
 	else if( isGrammar( m_target ) )
 		convertRGtoRG( );
@@ -102,8 +103,40 @@ void ConversionHandler::convertRG( void )
 
 // ----------------------------------------------------------------------------
 
-void ConversionHandler::convertFSMtoRE( void )
+void ConversionHandler::convertFSMtoRE(void)
 {
+    switch( m_target )
+    {
+    case REGULAR_EXPRESSION_FORMAL:
+        convertFSMtoREF();
+        break;
+    default:
+        convertFSMtoREU();
+        break;
+    }
+}
+
+void ConversionHandler::convertFSMtoREF(void)
+{
+	const automaton::Automaton automaton = alib::DataFactory::fromTokens<automaton::Automaton>(m_tokens);
+
+	switch( m_algorithm )
+	{
+	case BRZOZOWSKI_ALGEBRAIC:
+        break;
+	case STATE_ELIMINATION:
+	default: {
+			alib::DataFactory::toStdout(fa2re::StateEliminationFormal::convert(automaton));
+			break;
+		}
+	}
+
+}
+
+void ConversionHandler::convertFSMtoREU( void )
+{
+	const automaton::Automaton automaton = alib::DataFactory::fromTokens<automaton::Automaton>(m_tokens);
+
 	switch( m_algorithm )
 	{
 	case BRZOZOWSKI_ALGEBRAIC: {
@@ -123,9 +156,7 @@ void ConversionHandler::convertFSMtoRE( void )
 		}
 	case STATE_ELIMINATION:
 	default: {
-			/*fa2re::StateElimination conv( fsm );
-			regexp::UnboundedRegExp re = conv.convert();
-			alib::DataFactory::toStdout(re);*/
+			alib::DataFactory::toStdout(fa2re::StateEliminationUnbounded::convert(automaton));
 			break;
 		}
 	}
@@ -368,7 +399,9 @@ ConversionHandler::TFormalism ConversionHandler::parseFormalismFromString( const
 		return FINITE_AUTOMATON;
 
 	if( target == "re" || target == "regexp" || target == "regex" )
-		return REGULAR_EXPRESSION;
+		return REGULAR_EXPRESSION_UNBOUNDED;
+    if (target == "formalre" || target == "formalregexp" || target == "formalregex")
+        return REGULAR_EXPRESSION_FORMAL;
 
 	if( target == "rrg" || target == "rg" || target == "grammar" )
 		return RIGHT_REGULAR_GRAMMAR;
@@ -387,7 +420,10 @@ ConversionHandler::TFormalism ConversionHandler::parseFormalismFromTokens( void
 		return FINITE_AUTOMATON;
 
 	if( xmlMark == "regexp" || xmlMark == "unboundedRegexp" )
-		return REGULAR_EXPRESSION;
+		return REGULAR_EXPRESSION_UNBOUNDED;
+
+    if(xmlMark == "formalRegExp")
+        return REGULAR_EXPRESSION_FORMAL;
 
 	if( xmlMark == "RightRG" )
 		return RIGHT_REGULAR_GRAMMAR;
diff --git a/aconversions2/src/ConversionHandler.h b/aconversions2/src/ConversionHandler.h
index 8c1a383754..c8ce539e0f 100644
--- a/aconversions2/src/ConversionHandler.h
+++ b/aconversions2/src/ConversionHandler.h
@@ -69,13 +69,16 @@ public:
         LEFT_LINEAR_GRAMMAR,
         RIGHT_LINEAR_GRAMMAR,
 
-        REGULAR_EXPRESSION,
+        REGULAR_EXPRESSION_UNBOUNDED,
+        REGULAR_EXPRESSION_FORMAL,
 
         UNKNOWN,
     };
     #define isGrammar(x) ( (x) == REGULAR_GRAMMAR || (x) == LEFT_REGULAR_GRAMMAR || (x) == RIGHT_REGULAR_GRAMMAR \
                         || (x) == LINEAR_GRAMMAR  || (x) == LEFT_LINEAR_GRAMMAR  || (x) == RIGHT_LINEAR_GRAMMAR )
 
+    #define isRegExp(x) ( (x) == REGULAR_EXPRESSION_FORMAL || (x) == REGULAR_EXPRESSION_UNBOUNDED )
+
     ConversionHandler( std::list<sax::Token> & tokens, const std::string & target, const std::string & algorithm, std::ostream & out );
     void convert( void );
 
@@ -86,6 +89,8 @@ private:
 
     void convertFSM( void );
     void convertFSMtoRE( void );
+    void convertFSMtoREF( void );
+    void convertFSMtoREU( void );
     void convertFSMtoRG( void );
     void convertFSMtoLRG( void );
     void convertFSMtoRRG( void );
diff --git a/alib2algo/src/conversions/fa2re/formal/StateEliminationFormal.cpp b/alib2algo/src/conversions/fa2re/formal/StateEliminationFormal.cpp
new file mode 100644
index 0000000000..b7302c366a
--- /dev/null
+++ b/alib2algo/src/conversions/fa2re/formal/StateEliminationFormal.cpp
@@ -0,0 +1,292 @@
+/*
+ * StateEliminationFormal.cpp
+ *
+ *  Created on: 9. 2. 2014
+ *      Author: Tomas Pecka
+ */
+
+#include "StateEliminationFormal.h"
+
+#include <regexp/formal/FormalRegExp.h>
+#include <label/StringLabel.h>
+#include <exception/AlibException.h>
+
+#include "../../../regexp/RegExpOptimize.h"
+
+namespace fa2re
+{
+
+template<class T>
+regexp::RegExp StateEliminationFormal::convert(const T& automaton)
+{
+    if(automaton.getFinalStates().size() == 0)
+        return regexp::RegExp{regexp::FormalRegExp(regexp::FormalRegExpEmpty())};
+
+    // steps 1 + 2
+    automaton::ExtendedNFA extendedAutomaton = constructExtendedNFA(automaton);
+    extendExtendedNFA(extendedAutomaton);
+
+    // step 3 - Exterminate!
+    // select all states that are neither final nor initial
+    std::set<automaton::State> statesToEliminate = extendedAutomaton.getStates();
+    statesToEliminate.erase(*extendedAutomaton.getInitialStates().begin());
+    statesToEliminate.erase(*extendedAutomaton.getFinalStates().begin());
+
+    for(const auto& state : statesToEliminate)
+        extendedAutomaton = eliminateState(extendedAutomaton, state);
+
+    // step 4
+    //regexp::RegExpOptimize opt;
+
+    regexp::FormalRegExpConcatenation concat(
+        std::move(*transition(extendedAutomaton, *extendedAutomaton.getInitialStates().begin(), *extendedAutomaton.getFinalStates().begin())),
+        regexp::FormalRegExpIteration(std::move(*transition(extendedAutomaton, *extendedAutomaton.getFinalStates().begin(), *extendedAutomaton.getFinalStates().begin())))
+        );
+    //return regexp::RegExp{opt.optimize(regexp::FormalRegExp(concat))};
+    return regexp::RegExp{regexp::FormalRegExp(concat)};
+}
+
+template<>
+regexp::RegExp StateEliminationFormal::convert(const automaton::Automaton& automaton)
+{
+    regexp::RegExp* out = NULL;
+    automaton.getData().Accept((void*) &out, StateEliminationFormal::STATE_ELIMINATION);
+    regexp::RegExp res = std::move(*out);
+    delete out;
+    return res;
+}
+
+
+
+void StateEliminationFormal::Visit(void*, const automaton::UnknownAutomaton&) const
+{
+    throw exception::AlibException("Unsupported automaton type UnknownAutomaton");
+}
+
+void StateEliminationFormal::Visit(void* data, const automaton::EpsilonNFA& automaton) const
+{
+    regexp::RegExp* & out = *((regexp::RegExp**) data);
+    out = new regexp::RegExp(convert(automaton));
+}
+
+void StateEliminationFormal::Visit(void* data, const automaton::NFA& automaton) const
+{
+    regexp::RegExp* & out = *((regexp::RegExp**) data);
+    out = new regexp::RegExp(convert(automaton));
+}
+
+void StateEliminationFormal::Visit(void* data, const automaton::DFA& automaton) const
+{
+    regexp::RegExp* & out = *((regexp::RegExp**) data);
+    out = new regexp::RegExp(convert(automaton));
+}
+
+void StateEliminationFormal::Visit(void* data, const automaton::ExtendedNFA& automaton) const
+{
+    regexp::RegExp* & out = *((regexp::RegExp**) data);
+    out = new regexp::RegExp(convert(automaton));
+}
+
+void StateEliminationFormal::Visit(void*, const automaton::CompactNFA& ) const
+{
+    throw exception::AlibException("Unsupported automaton type CompactNFA");
+}
+
+void StateEliminationFormal::Visit(void*, const automaton::NPDA&) const
+{
+    throw exception::AlibException("Unsupported automaton type NPDA");
+}
+
+void StateEliminationFormal::Visit(void*, const automaton::SinglePopNPDA&) const
+{
+    throw exception::AlibException("Unsupported automaton type SinglePopNPDA");
+}
+
+void StateEliminationFormal::Visit(void*, const automaton::OneTapeDTM&) const
+{
+    throw exception::AlibException("Unsupported automaton type OneTapeDTM");
+}
+
+
+automaton::ExtendedNFA StateEliminationFormal::eliminateState(const automaton::ExtendedNFA& extendedAutomaton, const automaton::State& q)
+{
+    automaton::ExtendedNFA newAutomaton;
+    newAutomaton.setStates(extendedAutomaton.getStates());
+    newAutomaton.removeState(q); // preserve all states but q (the one to eliminate)
+    newAutomaton.setInputSymbols(extendedAutomaton.getInputAlphabet());
+    newAutomaton.setInitialStates(extendedAutomaton.getInitialStates()); // sure that q is neither initial nor final (follows from step 2 - extending ExtendedNFA)
+    newAutomaton.setFinalStates(extendedAutomaton.getFinalStates());
+
+    // regexp::RegExpOptimize opt;
+
+    for(const auto& p: newAutomaton.getStates())
+    {
+        for(const auto& r : newAutomaton.getStates())
+        {
+            regexp::FormalRegExpConcatenation concat(
+                std::move(*transition(extendedAutomaton, p, q)),
+                regexp::FormalRegExpConcatenation(
+                    regexp::FormalRegExpIteration(std::move(*transition(extendedAutomaton, q, q))),
+                    std::move(*transition(extendedAutomaton, q, r))
+                )
+            );
+
+            regexp::FormalRegExpAlternation alt(
+                std::move(*transition(extendedAutomaton, p, r)),
+                concat
+            );
+
+            // regexp::FormalRegExp transitionRegExp(opt.optimize(regexp::FormalRegExp(alt)));
+            regexp::FormalRegExp transitionRegExp(alt);
+            transitionRegExp.setAlphabet(extendedAutomaton.getInputAlphabet());
+            newAutomaton.addTransition(p, regexp::RegExp{transitionRegExp}, r);
+        }
+    }
+
+    return newAutomaton;
+}
+
+const regexp::FormalRegExpElement* StateEliminationFormal::transition(const automaton::ExtendedNFA& automaton, const automaton::State& from, const automaton::State& to)
+{
+    std::vector<regexp::FormalRegExpElement*> alt;
+
+    for(const auto& transition: automaton.getTransitionsFromState(from))
+        if(transition.second.count(to) > 0)
+            alt.push_back(static_cast<const regexp::FormalRegExp&>(transition.first.second.getData()).getRegExp().clone());
+
+    size_t size = alt.size();
+    if(size == 0) return new regexp::FormalRegExpEmpty();
+    if(size == 1) return alt.at(0);
+
+    regexp::FormalRegExpAlternation* ret = new regexp::FormalRegExpAlternation(std::move(*alt.at(--size)), std::move(*alt.at(--size)));
+    while(size)
+    {
+        regexp::FormalRegExpElement* left = alt.at(--size);
+        regexp::FormalRegExpElement* right = ret;
+
+        ret = new regexp::FormalRegExpAlternation(std::move(*left), std::move(*right));
+        delete left;
+        delete right;
+    }
+
+    return ret;
+}
+
+template<>
+automaton::ExtendedNFA StateEliminationFormal::constructExtendedNFA(const automaton::NFA& automaton)
+{
+    automaton::ExtendedNFA extendedAutomaton;
+    extendedAutomaton.setStates(automaton.getStates());
+    extendedAutomaton.setInputSymbols(automaton.getInputAlphabet());
+    extendedAutomaton.setInitialStates(automaton.getInitialStates());
+    extendedAutomaton.setFinalStates(automaton.getFinalStates());
+
+    for(const auto& transition : automaton.getTransitions()) // pair<State, symb/eps> -> set<State>
+    {
+        regexp::FormalRegExp regexp(regexp::FormalRegExpSymbol(transition.first.second));
+        regexp.setAlphabet(extendedAutomaton.getInputAlphabet());
+
+        for(const auto& to : transition.second)
+            extendedAutomaton.addTransition(transition.first.first, regexp::RegExp{regexp}, to);
+    }
+
+    return extendedAutomaton;
+}
+
+template<>
+automaton::ExtendedNFA StateEliminationFormal::constructExtendedNFA(const automaton::DFA& automaton)
+{
+    automaton::ExtendedNFA extendedAutomaton;
+    extendedAutomaton.setStates(automaton.getStates());
+    extendedAutomaton.setInputSymbols(automaton.getInputAlphabet());
+    extendedAutomaton.setInitialStates(std::set<automaton::State>{automaton.getInitialState()});
+    extendedAutomaton.setFinalStates(automaton.getFinalStates());
+
+    for(const auto& transition : automaton.getTransitions()) // pair<State, symb/eps> -> set<State>
+    {
+        regexp::FormalRegExp regexp(regexp::FormalRegExpSymbol(transition.first.second));
+        regexp.setAlphabet(extendedAutomaton.getInputAlphabet());
+        extendedAutomaton.addTransition(transition.first.first, regexp::RegExp{regexp}, transition.second);
+    }
+
+    return extendedAutomaton;
+}
+
+template<>
+automaton::ExtendedNFA StateEliminationFormal::constructExtendedNFA(const automaton::EpsilonNFA& automaton)
+{
+    automaton::ExtendedNFA extendedAutomaton;
+    extendedAutomaton.setStates(automaton.getStates());
+    extendedAutomaton.setInputSymbols(automaton.getInputAlphabet());
+    extendedAutomaton.setInitialStates(automaton.getInitialStates());
+    extendedAutomaton.setFinalStates(automaton.getFinalStates());
+
+    for(const auto& transition : automaton.getSymbolTransitions()) // pair<State, symb/eps> -> set<State>
+    {
+        regexp::FormalRegExp regexp(regexp::FormalRegExpSymbol(transition.first.second));
+        regexp.setAlphabet(extendedAutomaton.getInputAlphabet());
+
+        for(const auto& to : transition.second)
+            extendedAutomaton.addTransition(transition.first.first, regexp::RegExp{regexp}, to);
+    }
+    for(const auto& transition : automaton.getEpsilonTransitions())
+    {
+        regexp::FormalRegExp regexp;
+        regexp.setRegExp(regexp::FormalRegExpEpsilon());
+        regexp.setAlphabet(extendedAutomaton.getInputAlphabet());
+
+        for(const auto& to : transition.second)
+            extendedAutomaton.addTransition(transition.first, regexp::RegExp{regexp}, to);
+    }
+
+    return extendedAutomaton;
+}
+
+template<>
+automaton::ExtendedNFA StateEliminationFormal::constructExtendedNFA(const automaton::ExtendedNFA& automaton)
+{
+    return automaton;
+}
+
+void StateEliminationFormal::extendExtendedNFA(automaton::ExtendedNFA& automaton)
+{
+    const automaton::State& initState = *automaton.getInitialStates().begin();
+    if(automaton.getFinalStates().count(initState) > 0 || automaton.getTransitionsToState(initState).size() > 0 )
+    {
+        const automaton::State q0 = automaton::createUniqueState(initState, automaton.getStates());
+        automaton.addState(q0);
+
+        regexp::FormalRegExp regexp;
+        regexp.setRegExp(regexp::FormalRegExpEpsilon());
+        regexp.setAlphabet(automaton.getInputAlphabet());
+        automaton.addTransition(q0, regexp::RegExp{regexp}, initState);
+
+        const std::set<automaton::State> automatonInitialStates = automaton.getInitialStates();
+        for(const auto& state: automatonInitialStates)
+            automaton.removeInitialState(state);
+        automaton.addInitialState(q0);
+    }
+
+    if(automaton.getFinalStates().size() > 1)
+    {
+        const automaton::State f = automaton::createUniqueState(automaton::State(label::Label(label::StringLabel("f"))), automaton.getStates());
+        automaton.addState(f);
+
+        const std::set<automaton::State> automatonFinalStates = automaton.getFinalStates();
+        for(const auto &state : automatonFinalStates)
+        {
+            regexp::FormalRegExp regexp;
+            regexp.setRegExp(regexp::FormalRegExpEpsilon());
+            regexp.setAlphabet(automaton.getInputAlphabet());
+            automaton.addTransition(state, regexp::RegExp{regexp}, f);
+
+            automaton.removeFinalState(state);
+        }
+
+        automaton.addFinalState(f);
+    }
+}
+
+const StateEliminationFormal StateEliminationFormal::STATE_ELIMINATION;
+
+} /* namespace fa2re */
diff --git a/alib2algo/src/conversions/fa2re/formal/StateEliminationFormal.h b/alib2algo/src/conversions/fa2re/formal/StateEliminationFormal.h
new file mode 100644
index 0000000000..594ffa7041
--- /dev/null
+++ b/alib2algo/src/conversions/fa2re/formal/StateEliminationFormal.h
@@ -0,0 +1,62 @@
+/*
+ * StateEliminationFormal.h
+ *
+ *  Created on: 9. 2. 2014
+ *      Author: Tomas Pecka
+ */
+
+#ifndef STATEELIMINATIONFORMAL_H_
+#define STATEELIMINATIONFORMAL_H_
+
+#include <regexp/RegExp.h>
+#include <regexp/formal/FormalRegExpElements.h>
+
+#include <automaton/Automaton.h>
+#include <automaton/FSM/DFA.h>
+#include <automaton/FSM/NFA.h>
+#include <automaton/FSM/EpsilonNFA.h>
+#include <automaton/FSM/ExtendedNFA.h>
+
+namespace fa2re
+{
+
+/**
+ * Converts FSM to RE using State Elimination algorithm.
+ * Source: Melichar 2.118
+ */
+class StateEliminationFormal : public automaton::VisitableAutomatonBase::const_visitor_type
+{
+public:
+    /**
+     * Performs conversion.
+     * @return regular expression equivalent to source NFA.
+     */
+    template<class T>
+    static regexp::RegExp convert(const T& automaton);
+
+private:
+    void Visit(void*, const automaton::UnknownAutomaton& automaton) const;
+    void Visit(void*, const automaton::EpsilonNFA& automaton) const;
+    void Visit(void*, const automaton::NFA& automaton) const;
+    void Visit(void*, const automaton::DFA& automaton) const;
+    void Visit(void*, const automaton::ExtendedNFA& automaton) const;
+    void Visit(void*, const automaton::CompactNFA& automaton) const;
+    void Visit(void*, const automaton::NPDA& automaton) const;
+    void Visit(void*, const automaton::SinglePopNPDA& automaton) const;
+    void Visit(void*, const automaton::OneTapeDTM& automaton) const;
+
+    template<class T>
+    static automaton::ExtendedNFA constructExtendedNFA(const T& automaton);
+
+    static void extendExtendedNFA(automaton::ExtendedNFA& automaton);
+
+    static const regexp::FormalRegExpElement* transition(const automaton::ExtendedNFA& automaton, const automaton::State& from, const automaton::State& to);
+
+    static automaton::ExtendedNFA eliminateState(const automaton::ExtendedNFA& extendedAutomaton, const automaton::State& state);
+
+    static const StateEliminationFormal STATE_ELIMINATION;
+};
+
+} /* namespace fa2re */
+
+#endif /* STATEELIMINATIONFORMAL_H_ */
diff --git a/alib2algo/src/conversions/fa2re/unbounded/StateEliminationUnbounded.cpp b/alib2algo/src/conversions/fa2re/unbounded/StateEliminationUnbounded.cpp
new file mode 100644
index 0000000000..311a8c74e0
--- /dev/null
+++ b/alib2algo/src/conversions/fa2re/unbounded/StateEliminationUnbounded.cpp
@@ -0,0 +1,272 @@
+/*
+ * StateEliminationUnbounded.cpp
+ *
+ *  Created on: 9. 2. 2014
+ *      Author: Tomas Pecka
+ */
+
+#include "StateEliminationUnbounded.h"
+
+#include <regexp/unbounded/UnboundedRegExp.h>
+#include <label/StringLabel.h>
+#include <exception/AlibException.h>
+#include <factory/DataFactory.hpp>
+
+#include "../../../regexp/RegExpOptimize.h"
+
+namespace fa2re
+{
+
+template<class T>
+regexp::RegExp StateEliminationUnbounded::convert(const T& automaton)
+{
+    if(automaton.getFinalStates().size() == 0)
+        return regexp::RegExp{regexp::UnboundedRegExp(regexp::UnboundedRegExpEmpty())};
+
+    // steps 1 + 2
+    automaton::ExtendedNFA extendedAutomaton = constructExtendedNFA(automaton);
+    extendExtendedNFA(extendedAutomaton);
+
+    // step 3 - Exterminate!
+    // select all states that are neither final nor initial
+    std::set<automaton::State> statesToEliminate = extendedAutomaton.getStates();
+    statesToEliminate.erase(*extendedAutomaton.getInitialStates().begin());
+    statesToEliminate.erase(*extendedAutomaton.getFinalStates().begin());
+
+    for(const auto& state : statesToEliminate)
+        extendedAutomaton = eliminateState(extendedAutomaton, state);
+
+    // step 4
+    regexp::RegExpOptimize opt;
+
+    regexp::UnboundedRegExpConcatenation concat;
+    concat.appendElement(std::move(*transition(extendedAutomaton, *extendedAutomaton.getInitialStates().begin(), *extendedAutomaton.getFinalStates().begin())));
+    concat.appendElement(regexp::UnboundedRegExpIteration(std::move(*transition(extendedAutomaton, *extendedAutomaton.getFinalStates().begin(), *extendedAutomaton.getFinalStates().begin()))));
+    return regexp::RegExp{opt.optimize(regexp::UnboundedRegExp(concat))};
+}
+
+template<>
+regexp::RegExp StateEliminationUnbounded::convert(const automaton::Automaton& automaton)
+{
+    regexp::RegExp* out = NULL;
+    automaton.getData().Accept((void*) &out, StateEliminationUnbounded::STATE_ELIMINATION);
+    regexp::RegExp res = std::move(*out);
+    delete out;
+    return res;
+}
+
+
+
+void StateEliminationUnbounded::Visit(void*, const automaton::UnknownAutomaton&) const
+{
+    throw exception::AlibException("Unsupported automaton type UnknownAutomaton");
+}
+
+void StateEliminationUnbounded::Visit(void* data, const automaton::EpsilonNFA& automaton) const
+{
+    regexp::RegExp* & out = *((regexp::RegExp**) data);
+    out = new regexp::RegExp(convert(automaton));
+}
+
+void StateEliminationUnbounded::Visit(void* data, const automaton::NFA& automaton) const
+{
+    regexp::RegExp* & out = *((regexp::RegExp**) data);
+    out = new regexp::RegExp(convert(automaton));
+}
+
+void StateEliminationUnbounded::Visit(void* data, const automaton::DFA& automaton) const
+{
+    regexp::RegExp* & out = *((regexp::RegExp**) data);
+    out = new regexp::RegExp(convert(automaton));
+}
+
+void StateEliminationUnbounded::Visit(void* data, const automaton::ExtendedNFA& automaton) const
+{
+    regexp::RegExp* & out = *((regexp::RegExp**) data);
+    out = new regexp::RegExp(convert(automaton));
+}
+
+void StateEliminationUnbounded::Visit(void*, const automaton::CompactNFA& ) const
+{
+    throw exception::AlibException("Unsupported automaton type CompactNFA");
+}
+
+void StateEliminationUnbounded::Visit(void*, const automaton::NPDA&) const
+{
+    throw exception::AlibException("Unsupported automaton type NPDA");
+}
+
+void StateEliminationUnbounded::Visit(void*, const automaton::SinglePopNPDA&) const
+{
+    throw exception::AlibException("Unsupported automaton type SinglePopNPDA");
+}
+
+void StateEliminationUnbounded::Visit(void*, const automaton::OneTapeDTM&) const
+{
+    throw exception::AlibException("Unsupported automaton type OneTapeDTM");
+}
+
+
+automaton::ExtendedNFA StateEliminationUnbounded::eliminateState(const automaton::ExtendedNFA& extendedAutomaton, const automaton::State& q)
+{
+    automaton::ExtendedNFA newAutomaton;
+    newAutomaton.setStates(extendedAutomaton.getStates());
+    newAutomaton.removeState(q); // preserve all states but q (the one to eliminate)
+    newAutomaton.setInputSymbols(extendedAutomaton.getInputAlphabet());
+    newAutomaton.setInitialStates(extendedAutomaton.getInitialStates()); // sure that q is neither initial nor final (follows from step 2 - extending ExtendedNFA)
+    newAutomaton.setFinalStates(extendedAutomaton.getFinalStates());
+
+    regexp::RegExpOptimize opt;
+
+    for(const auto& p: newAutomaton.getStates())
+    {
+        for(const auto& r : newAutomaton.getStates())
+        {
+            regexp::UnboundedRegExpConcatenation concat;
+            concat.appendElement(std::move(*transition(extendedAutomaton, p, q)));
+            concat.appendElement(regexp::UnboundedRegExpIteration(std::move(*transition(extendedAutomaton, q, q))));
+            concat.appendElement(std::move(*transition(extendedAutomaton, q, r)));
+
+            regexp::UnboundedRegExpAlternation alt;
+            alt.appendElement(std::move(*transition(extendedAutomaton, p, r)));
+            alt.appendElement(concat);
+
+            regexp::UnboundedRegExp transitionRegExp(opt.optimize(regexp::UnboundedRegExp(alt)));
+            transitionRegExp.setAlphabet(extendedAutomaton.getInputAlphabet());
+            newAutomaton.addTransition(p, regexp::RegExp{transitionRegExp}, r);
+        }
+    }
+
+    return newAutomaton;
+}
+
+const regexp::UnboundedRegExpElement* StateEliminationUnbounded::transition(const automaton::ExtendedNFA& automaton, const automaton::State& from, const automaton::State& to)
+{
+    regexp::RegExpOptimize opt;
+    regexp::UnboundedRegExpAlternation ret;
+
+    for(const auto& transition: automaton.getTransitionsFromState(from))
+        if(transition.second.count(to) > 0)
+            ret.appendElement(static_cast<const regexp::UnboundedRegExp&>(transition.first.second.getData()).getRegExp());
+
+    return opt.optimize(regexp::UnboundedRegExp(ret)).getRegExp().clone();
+}
+
+template<>
+automaton::ExtendedNFA StateEliminationUnbounded::constructExtendedNFA(const automaton::NFA& automaton)
+{
+    automaton::ExtendedNFA extendedAutomaton;
+    extendedAutomaton.setStates(automaton.getStates());
+    extendedAutomaton.setInputSymbols(automaton.getInputAlphabet());
+    extendedAutomaton.setInitialStates(automaton.getInitialStates());
+    extendedAutomaton.setFinalStates(automaton.getFinalStates());
+
+    for(const auto& transition : automaton.getTransitions()) // pair<State, symb/eps> -> set<State>
+    {
+        regexp::UnboundedRegExp regexp(regexp::UnboundedRegExpSymbol(transition.first.second));
+        regexp.setAlphabet(extendedAutomaton.getInputAlphabet());
+
+        for(const auto& to : transition.second)
+            extendedAutomaton.addTransition(transition.first.first, regexp::RegExp{regexp}, to);
+    }
+
+    return extendedAutomaton;
+}
+
+template<>
+automaton::ExtendedNFA StateEliminationUnbounded::constructExtendedNFA(const automaton::DFA& automaton)
+{
+    automaton::ExtendedNFA extendedAutomaton;
+    extendedAutomaton.setStates(automaton.getStates());
+    extendedAutomaton.setInputSymbols(automaton.getInputAlphabet());
+    extendedAutomaton.setInitialStates(std::set<automaton::State>{automaton.getInitialState()});
+    extendedAutomaton.setFinalStates(automaton.getFinalStates());
+
+    for(const auto& transition : automaton.getTransitions()) // pair<State, symb/eps> -> set<State>
+    {
+        regexp::UnboundedRegExp regexp(regexp::UnboundedRegExpSymbol(transition.first.second));
+        regexp.setAlphabet(extendedAutomaton.getInputAlphabet());
+        extendedAutomaton.addTransition(transition.first.first, regexp::RegExp{regexp}, transition.second);
+    }
+
+    return extendedAutomaton;
+}
+
+template<>
+automaton::ExtendedNFA StateEliminationUnbounded::constructExtendedNFA(const automaton::EpsilonNFA& automaton)
+{
+    automaton::ExtendedNFA extendedAutomaton;
+    extendedAutomaton.setStates(automaton.getStates());
+    extendedAutomaton.setInputSymbols(automaton.getInputAlphabet());
+    extendedAutomaton.setInitialStates(automaton.getInitialStates());
+    extendedAutomaton.setFinalStates(automaton.getFinalStates());
+
+    for(const auto& transition : automaton.getSymbolTransitions()) // pair<State, symb/eps> -> set<State>
+    {
+        regexp::UnboundedRegExp regexp(regexp::UnboundedRegExpSymbol(transition.first.second));
+        regexp.setAlphabet(extendedAutomaton.getInputAlphabet());
+
+        for(const auto& to : transition.second)
+            extendedAutomaton.addTransition(transition.first.first, regexp::RegExp{regexp}, to);
+    }
+    for(const auto& transition : automaton.getEpsilonTransitions())
+    {
+        regexp::UnboundedRegExp regexp;
+        regexp.setRegExp(regexp::UnboundedRegExpEpsilon());
+        regexp.setAlphabet(extendedAutomaton.getInputAlphabet());
+
+        for(const auto& to : transition.second)
+            extendedAutomaton.addTransition(transition.first, regexp::RegExp{regexp}, to);
+    }
+
+    return extendedAutomaton;
+}
+
+template<>
+automaton::ExtendedNFA StateEliminationUnbounded::constructExtendedNFA(const automaton::ExtendedNFA& automaton)
+{
+    return automaton;
+}
+
+void StateEliminationUnbounded::extendExtendedNFA(automaton::ExtendedNFA& automaton)
+{
+    const automaton::State& initState = *automaton.getInitialStates().begin();
+    if(automaton.getFinalStates().count(initState) > 0 || automaton.getTransitionsToState(initState).size() > 0 )
+    {
+        const automaton::State q0 = automaton::createUniqueState(initState, automaton.getStates());
+        automaton.addState(q0);
+
+        regexp::UnboundedRegExp regexp;
+        regexp.setRegExp(regexp::UnboundedRegExpEpsilon());
+        regexp.setAlphabet(automaton.getInputAlphabet());
+        automaton.addTransition(q0, regexp::RegExp{regexp}, initState);
+
+        const std::set<automaton::State> automatonInitialStates = automaton.getInitialStates();
+        for(const auto& state: automatonInitialStates)
+            automaton.removeInitialState(state);
+        automaton.addInitialState(q0);
+    }
+
+    if(automaton.getFinalStates().size() > 1)
+    {
+        const automaton::State f = automaton::createUniqueState(automaton::State(label::Label(label::StringLabel("f"))), automaton.getStates());
+        automaton.addState(f);
+
+        const std::set<automaton::State> automatonFinalStates = automaton.getFinalStates();
+        for(const auto &state : automatonFinalStates)
+        {
+            regexp::UnboundedRegExp regexp;
+            regexp.setRegExp(regexp::UnboundedRegExpEpsilon());
+            regexp.setAlphabet(automaton.getInputAlphabet());
+            automaton.addTransition(state, regexp::RegExp{regexp}, f);
+
+            automaton.removeFinalState(state);
+        }
+
+        automaton.addFinalState(f);
+    }
+}
+
+const StateEliminationUnbounded StateEliminationUnbounded::STATE_ELIMINATION;
+
+} /* namespace fa2re */
diff --git a/alib2algo/src/conversions/fa2re/unbounded/StateEliminationUnbounded.h b/alib2algo/src/conversions/fa2re/unbounded/StateEliminationUnbounded.h
new file mode 100644
index 0000000000..f5e081814c
--- /dev/null
+++ b/alib2algo/src/conversions/fa2re/unbounded/StateEliminationUnbounded.h
@@ -0,0 +1,62 @@
+/*
+ * StateEliminationUnbounded.h
+ *
+ *  Created on: 9. 2. 2014
+ *      Author: Tomas Pecka
+ */
+
+#ifndef STATEELIMINATIONUNBOUNDED_H_
+#define STATEELIMINATIONUNBOUNDED_H_
+
+#include <regexp/RegExp.h>
+#include <regexp/unbounded/UnboundedRegExpElements.h>
+
+#include <automaton/Automaton.h>
+#include <automaton/FSM/DFA.h>
+#include <automaton/FSM/NFA.h>
+#include <automaton/FSM/EpsilonNFA.h>
+#include <automaton/FSM/ExtendedNFA.h>
+
+namespace fa2re
+{
+
+/**
+ * Converts FSM to RE using State Elimination algorithm.
+ * Source: Melichar 2.118
+ */
+class StateEliminationUnbounded : public automaton::VisitableAutomatonBase::const_visitor_type
+{
+public:
+    /**
+     * Performs conversion.
+     * @return regular expression equivalent to source NFA.
+     */
+    template<class T>
+    static regexp::RegExp convert(const T& automaton);
+
+private:
+    void Visit(void*, const automaton::UnknownAutomaton& automaton) const;
+    void Visit(void*, const automaton::EpsilonNFA& automaton) const;
+    void Visit(void*, const automaton::NFA& automaton) const;
+    void Visit(void*, const automaton::DFA& automaton) const;
+    void Visit(void*, const automaton::ExtendedNFA& automaton) const;
+    void Visit(void*, const automaton::CompactNFA& automaton) const;
+    void Visit(void*, const automaton::NPDA& automaton) const;
+    void Visit(void*, const automaton::SinglePopNPDA& automaton) const;
+    void Visit(void*, const automaton::OneTapeDTM& automaton) const;
+
+    template<class T>
+    static automaton::ExtendedNFA constructExtendedNFA(const T& automaton);
+
+    static void extendExtendedNFA(automaton::ExtendedNFA& automaton);
+
+    static const regexp::UnboundedRegExpElement* transition(const automaton::ExtendedNFA& automaton, const automaton::State& from, const automaton::State& to);
+
+    static automaton::ExtendedNFA eliminateState(const automaton::ExtendedNFA& extendedAutomaton, const automaton::State& state);
+
+    static const StateEliminationUnbounded STATE_ELIMINATION;
+};
+
+} /* namespace fa2re */
+
+#endif /* STATEELIMINATIONUNBOUNDED_H_ */
diff --git a/tests.aconversion.sh b/tests.aconversion.sh
index a433ccd025..73ecbc2d2d 100755
--- a/tests.aconversion.sh
+++ b/tests.aconversion.sh
@@ -85,7 +85,7 @@ function runTest2 {
 	fi
 }
 
-# $1 - aconversion sequence
+# $1 - aconversion2 sequence
 function runTest {
 	RES_GOOD=0
 	RES_FAIL=0
@@ -167,22 +167,22 @@ runTest "./aconversions2 -t LRG | ./aconversions2 -t RRG | ./aconversions2 -t FA
 runTest "./aconversions2 -t RE -a algebraic | ./aconversions2 -t FA -a brzozowski"
 runTest "./aconversions2 -t RE -a algebraic | ./aconversions2 -t FA -a thompson"
 runTest "./aconversions2 -t RE -a algebraic | ./aconversions2 -t FA -a glushkov "
-#runTest "./aconversion -t RE -a elimination | ./aconversion -t FA -a brzozowski"
-#runTest "./aconversion -t RE -a elimination | ./aconversion -t FA -a thompson"
-#runTest "./aconversion -t RE -a elimination | ./aconversion -t FA -a glushkov"
+runTest "./aconversions2 -t RE -a elimination | ./aconversions2 -t FA -a brzozowski"
+runTest "./aconversions2 -t RE -a elimination | ./aconversions2 -t FA -a thompson"
+runTest "./aconversions2 -t RE -a elimination | ./aconversions2 -t FA -a glushkov"
 
 # FA -> RE -> RRG -> LRG -> FA
 # covers: FA -> RE (Brz. algebraic, elimination), RE -> RRG ( Brz. derivation, Glushkov), RRG -> LRG, LRG -> FA
-# runTest "./aconversion -t RE -a algebraic | ./aconversion -t RRG -a brzozowski | ./aconversion -t LRG | ./aconversion -t FA"
-# runTest "./aconversion -t RE -a algebraic | ./aconversion -t RRG -a glushkov | ./aconversion -t LRG | ./aconversion -t FA"
-# runTest "./aconversion -t RE -a elimination | ./aconversion -t RRG -a brzozowski | ./aconversion -t LRG | ./aconversion -t FA"
-# runTest "./aconversion -t RE -a elimination | ./aconversion -t RRG -a glushkov | ./aconversion -t LRG | ./aconversion -t FA"
+runTest "./aconversions2 -t RE -a algebraic | ./aconversions2 -t RRG -a brzozowski | ./aconversion2 -t LRG | ./aconversion2 -t FA"
+runTest "./aconversions2 -ts2-t RE -a algebraic | ./aconversions2 -t RRG -a glushkov | ./aconversion2 -t LRG | ./aconversion2 -t FA"
+runTest "./aconversions2 -ts2-t RE -a elimination | ./aconversions2 -t RRG -a brzozowski | ./aconversion2 -t LRG | ./aconversion2 -t FA"
+runTest "./aconversions2 -ts2-t RE -a elimination | ./aconversions2 -t RRG -a glushkov | ./aconversion2 -t LRG | ./aconversion2 -t FA"
 
 # FA -> RRG -> RE -> FA
 # covers: FA -> RRG, FA -> LRG, RRG -> RE, LRG -> RE, RE -> FA (Brz. derivation, Thompson, Glushkov)
-# runTest "./aconversion -t RRG | ./aconversion -t RE | ./aconversion -t FA -a brzozowski"
-# runTest "./aconversion -t LRG | ./aconversion -t RE | ./aconversion -t FA -a brzozowski"
-# runTest "./aconversion -t RRG | ./aconversion -t RE | ./aconversion -t FA -a thompson"
-# runTest "./aconversion -t LRG | ./aconversion -t RE | ./aconversion -t FA -a thompson"
-# runTest "./aconversion -t RRG | ./aconversion -t RE | ./aconversion -t FA -a glushkov"
-# runTest "./aconversion -t LRG | ./aconversion -t RE | ./aconversion -t FA -a glushkov"
+# runTest "./aconversion2 -t RRG | ./aconversion2 -t RE | ./aconversion2 -t FA -a brzozowski"
+# runTest "./aconversion2 -t LRG | ./aconversion2 -t RE | ./aconversion2 -t FA -a brzozowski"
+# runTest "./aconversion2 -t RRG | ./aconversion2 -t RE | ./aconversion2 -t FA -a thompson"
+# runTest "./aconversion2 -t LRG | ./aconversion2 -t RE | ./aconversion2 -t FA -a thompson"
+# runTest "./aconversion2 -t RRG | ./aconversion2 -t RE | ./aconversion2 -t FA -a glushkov"
+# runTest "./aconversion2 -t LRG | ./aconversion2 -t RE | ./aconversion2 -t FA -a glushkov"
-- 
GitLab