diff --git a/alib2algo/src/automaton/transform/AutomataConcatenationEpsilonTransition.cpp b/alib2algo/src/automaton/transform/AutomataConcatenationEpsilonTransition.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..199c99da24a805a2f5304213a0120ea5fc9d0086
--- /dev/null
+++ b/alib2algo/src/automaton/transform/AutomataConcatenationEpsilonTransition.cpp
@@ -0,0 +1,209 @@
+/*
+ * AutomataConcatenationEpsilonTransition.cpp
+ *
+ *  Created on: 20. 11. 2014
+ *	  Author: Tomas Pecka
+ */
+
+#include "AutomataConcatenationEpsilonTransition.h"
+#include <exception/AlibException.h>
+#include <label/Label.h>
+#include "common/PairLabel.h"
+
+#define AUTOMATON_FIRST  1
+#define AUTOMATON_SECOND 2
+
+namespace automaton
+{
+
+namespace transform
+{
+
+automaton::Automaton AutomataConcatenationEpsilonTransition::concatenation(const automaton::Automaton& first, const automaton::Automaton& second)
+{
+	AutomatonBase* out;
+	Accept((void*) &out, first.getData(), second.getData(), AutomataConcatenationEpsilonTransition::AUTOMATA_CONCATENATION_EPSILON_TRANSITION);
+	automaton::Automaton res(*out);
+	delete out;
+	return res;
+}
+
+automaton::EpsilonNFA AutomataConcatenationEpsilonTransition::concatenation(const automaton::DFA& first, const automaton::DFA& second)
+{
+	automaton::EpsilonNFA res(pairLabel(label::labelFrom(AUTOMATON_FIRST), first.getInitialState()));
+
+	for(const auto& symbol : first.getInputAlphabet())
+		res.addInputSymbol(symbol);
+	for(const auto& symbol : second.getInputAlphabet())
+		res.addInputSymbol(symbol);
+
+	for(const auto& q : first.getStates())
+		res.addState(pairLabel(label::labelFrom(AUTOMATON_FIRST), q));
+	for(const auto& q : second.getStates())
+		res.addState(pairLabel(label::labelFrom(AUTOMATON_SECOND), q));
+
+	for(const auto& q : second.getFinalStates())
+		res.addFinalState(pairLabel(label::labelFrom(AUTOMATON_SECOND), q));
+
+	for(const auto& t : first.getTransitions())
+		res.addTransition(pairLabel(label::labelFrom(AUTOMATON_FIRST), t.first.first), t.first.second, pairLabel(label::labelFrom(AUTOMATON_FIRST), t.second));
+
+	for(const auto& t : second.getTransitions())
+		res.addTransition(pairLabel(label::labelFrom(AUTOMATON_SECOND), t.first.first), t.first.second, pairLabel(label::labelFrom(AUTOMATON_SECOND), t.second));
+
+	for(const auto& q : first.getFinalStates())
+		res.addTransition(pairLabel(label::labelFrom(AUTOMATON_FIRST), q), pairLabel(label::labelFrom(AUTOMATON_SECOND), second.getInitialState()));
+
+	return res;
+}
+
+automaton::EpsilonNFA AutomataConcatenationEpsilonTransition::concatenation(const automaton::NFA& first, const automaton::NFA& second)
+{
+	automaton::EpsilonNFA res(pairLabel(label::labelFrom(AUTOMATON_FIRST), first.getInitialState()));
+
+	for(const auto& symbol : first.getInputAlphabet())
+		res.addInputSymbol(symbol);
+	for(const auto& symbol : second.getInputAlphabet())
+		res.addInputSymbol(symbol);
+
+	for(const auto& q : first.getStates())
+		res.addState(pairLabel(label::labelFrom(AUTOMATON_FIRST), q));
+	for(const auto& q : second.getStates())
+		res.addState(pairLabel(label::labelFrom(AUTOMATON_SECOND), q));
+
+	for(const auto& q : second.getFinalStates())
+		res.addFinalState(pairLabel(label::labelFrom(AUTOMATON_SECOND), q));
+
+	for(const auto& t : first.getTransitions())
+		for(const auto& q : t.second)
+			res.addTransition(pairLabel(label::labelFrom(AUTOMATON_FIRST), t.first.first), t.first.second, pairLabel(label::labelFrom(AUTOMATON_FIRST), q));
+
+	for(const auto& t : second.getTransitions())
+		for(const auto& q : t.second)
+			res.addTransition(pairLabel(label::labelFrom(AUTOMATON_SECOND), t.first.first), t.first.second, pairLabel(label::labelFrom(AUTOMATON_SECOND), q));
+
+	for(const auto& q : first.getFinalStates())
+		res.addTransition(pairLabel(label::labelFrom(AUTOMATON_FIRST), q), pairLabel(label::labelFrom(AUTOMATON_SECOND), second.getInitialState()));
+
+	return res;
+}
+
+automaton::EpsilonNFA AutomataConcatenationEpsilonTransition::concatenation(const automaton::EpsilonNFA& first, const automaton::EpsilonNFA& second)
+{
+	automaton::EpsilonNFA res(pairLabel(label::labelFrom(AUTOMATON_FIRST), first.getInitialState()));
+
+	for(const auto& symbol : first.getInputAlphabet())
+		res.addInputSymbol(symbol);
+	for(const auto& symbol : second.getInputAlphabet())
+		res.addInputSymbol(symbol);
+
+	for(const auto& q : first.getStates())
+		res.addState(pairLabel(label::labelFrom(AUTOMATON_FIRST), q));
+	for(const auto& q : second.getStates())
+		res.addState(pairLabel(label::labelFrom(AUTOMATON_SECOND), q));
+
+	for(const auto& q : second.getFinalStates())
+		res.addFinalState(pairLabel(label::labelFrom(AUTOMATON_SECOND), q));
+
+	for(const auto& t : first.getTransitions())
+		for(const auto& q : t.second)
+			res.addTransition(pairLabel(label::labelFrom(AUTOMATON_FIRST), t.first.first), t.first.second, pairLabel(label::labelFrom(AUTOMATON_FIRST), q));
+
+	for(const auto& t : second.getTransitions())
+		for(const auto& q : t.second)
+			res.addTransition(pairLabel(label::labelFrom(AUTOMATON_SECOND), t.first.first), t.first.second, pairLabel(label::labelFrom(AUTOMATON_SECOND), q));
+
+	for(const auto& q : first.getFinalStates())
+		res.addTransition(pairLabel(label::labelFrom(AUTOMATON_FIRST), q), pairLabel(label::labelFrom(AUTOMATON_SECOND), second.getInitialState()));
+
+	return res;
+}
+
+void AutomataConcatenationEpsilonTransition::Visit(void* data, const automaton::EpsilonNFA& first, const automaton::EpsilonNFA& second) const
+{
+	AutomatonBase* &ret = *(AutomatonBase**) data;
+	ret = std::move(AutomataConcatenationEpsilonTransition::concatenation(first, second)).plunder();
+}
+
+void AutomataConcatenationEpsilonTransition::Visit(void*, const automaton::CompactNFA&, const automaton::CompactNFA&) const
+{
+	throw exception::AlibException("Unsupported automaton type CompactNFA");
+}
+
+void AutomataConcatenationEpsilonTransition::Visit(void* data, const automaton::DFA& first, const automaton::DFA& second) const
+{
+	AutomatonBase* &ret = *(AutomatonBase**) data;
+	ret = std::move(AutomataConcatenationEpsilonTransition::concatenation(first, second)).plunder();
+}
+
+void AutomataConcatenationEpsilonTransition::Visit(void*, const automaton::ExtendedNFA&, const automaton::ExtendedNFA&) const
+{
+	throw exception::AlibException("Unsupported automaton type ExtendedNFA");
+}
+
+void AutomataConcatenationEpsilonTransition::Visit(void*, const automaton::MultiInitialStateNFA&, const automaton::MultiInitialStateNFA&) const
+{
+	throw exception::AlibException("Unsupported automaton type MultiInitialStateNFA");
+}
+
+void AutomataConcatenationEpsilonTransition::Visit(void* data, const automaton::NFA& first, const automaton::NFA& second) const
+{
+	AutomatonBase* &ret = *(AutomatonBase**) data;
+	ret = std::move(AutomataConcatenationEpsilonTransition::concatenation(first, second)).plunder();
+}
+
+void AutomataConcatenationEpsilonTransition::Visit(void*, const automaton::DPDA&, const automaton::DPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type DPDA");
+}
+
+void AutomataConcatenationEpsilonTransition::Visit(void*, const automaton::NPDA&, const automaton::NPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type NPDA");
+}
+
+void AutomataConcatenationEpsilonTransition::Visit(void*, const automaton::InputDrivenNPDA&, const automaton::InputDrivenNPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type InputDrivenNPDA");
+}
+
+void AutomataConcatenationEpsilonTransition::Visit(void*, const automaton::RealTimeHeightDeterministicDPDA&, const automaton::RealTimeHeightDeterministicDPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type RealTimeHeightDeterministicDPDA");
+}
+
+void AutomataConcatenationEpsilonTransition::Visit(void*, const automaton::RealTimeHeightDeterministicNPDA&, const automaton::RealTimeHeightDeterministicNPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type RealTimeHeightDeterministicNPDA");
+}
+
+void AutomataConcatenationEpsilonTransition::Visit(void*, const automaton::SinglePopNPDA&, const automaton::SinglePopNPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type SinglePopNPDA");
+}
+
+void AutomataConcatenationEpsilonTransition::Visit(void*, const automaton::SinglePopDPDA&, const automaton::SinglePopDPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type SinglePopDPDA");
+}
+
+void AutomataConcatenationEpsilonTransition::Visit(void*, const automaton::VisiblyPushdownDPDA&, const automaton::VisiblyPushdownDPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type VisiblyPushdownDPDA");
+}
+
+void AutomataConcatenationEpsilonTransition::Visit(void*, const automaton::VisiblyPushdownNPDA&, const automaton::VisiblyPushdownNPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type VisiblyPushdownNPDA");
+}
+
+void AutomataConcatenationEpsilonTransition::Visit(void*, const automaton::OneTapeDTM&, const automaton::OneTapeDTM&) const
+{
+	throw exception::AlibException("Unsupported automaton type OneTapeDTM");
+}
+
+const AutomataConcatenationEpsilonTransition AutomataConcatenationEpsilonTransition::AUTOMATA_CONCATENATION_EPSILON_TRANSITION;
+
+} /* namespace transform */
+
+} /* namespace automaton */
diff --git a/alib2algo/src/automaton/transform/AutomataConcatenationEpsilonTransition.h b/alib2algo/src/automaton/transform/AutomataConcatenationEpsilonTransition.h
new file mode 100644
index 0000000000000000000000000000000000000000..b1c5c547030ba09746df2c123448edf93f30ba82
--- /dev/null
+++ b/alib2algo/src/automaton/transform/AutomataConcatenationEpsilonTransition.h
@@ -0,0 +1,60 @@
+/*
+ * AutomataConcatenationEpsilonTransition.h
+ *
+ *  Created on: 20. 11. 2014
+ *	  Author: Tomas Pecka
+ */
+
+#ifndef AUTOMATA_CONCATENATION_EPSILON_TRANSITION_H_
+#define AUTOMATA_CONCATENATION_EPSILON_TRANSITION_H_
+
+#include <automaton/Automaton.h>
+#include <automaton/FSM/EpsilonNFA.h>
+
+namespace automaton
+{
+
+namespace transform
+{
+
+/**
+ * Concatenates two automata.
+ *  - For finite automata A1, A2, we create automaton L accepting L(A1).L(A2)
+ */
+class AutomataConcatenationEpsilonTransition : public automaton::VisitableAutomatonBase::const_promoting_visitor_type
+{
+public:
+	static automaton::Automaton concatenation(const automaton::Automaton& first, const automaton::Automaton& second);
+
+	static automaton::EpsilonNFA concatenation(const automaton::DFA& first, const automaton::DFA& second);
+	static automaton::EpsilonNFA concatenation(const automaton::NFA& first, const automaton::NFA& second);
+	static automaton::EpsilonNFA concatenation(const automaton::EpsilonNFA& first, const automaton::EpsilonNFA& second);
+
+private:
+	void Visit(void* data, const automaton::CompactNFA& first, const automaton::CompactNFA& second) const;
+	void Visit(void* data, const automaton::DFA& first, const automaton::DFA& second) const;
+	void Visit(void* data, const automaton::EpsilonNFA& first, const automaton::EpsilonNFA& second) const;
+	void Visit(void* data, const automaton::ExtendedNFA& first, const automaton::ExtendedNFA& second) const;
+	void Visit(void* data, const automaton::MultiInitialStateNFA& first, const automaton::MultiInitialStateNFA& second) const;
+	void Visit(void* data, const automaton::NFA& first, const automaton::NFA& second) const;
+
+	void Visit(void* data, const automaton::DPDA& first, const automaton::DPDA& second) const;
+	void Visit(void* data, const automaton::NPDA& first, const automaton::NPDA& second) const;
+	void Visit(void* data, const automaton::InputDrivenNPDA& first, const automaton::InputDrivenNPDA& second) const;
+	void Visit(void* data, const automaton::RealTimeHeightDeterministicDPDA& first, const automaton::RealTimeHeightDeterministicDPDA& second) const;
+	void Visit(void* data, const automaton::RealTimeHeightDeterministicNPDA& first, const automaton::RealTimeHeightDeterministicNPDA& second) const;
+	void Visit(void* data, const automaton::SinglePopNPDA& first, const automaton::SinglePopNPDA& second) const;
+	void Visit(void* data, const automaton::SinglePopDPDA& first, const automaton::SinglePopDPDA& second) const;
+	void Visit(void* data, const automaton::VisiblyPushdownDPDA& first, const automaton::VisiblyPushdownDPDA& second) const;
+	void Visit(void* data, const automaton::VisiblyPushdownNPDA& first, const automaton::VisiblyPushdownNPDA& second) const;
+
+	void Visit(void* data, const automaton::OneTapeDTM& first, const automaton::OneTapeDTM& second) const;
+
+	static const AutomataConcatenationEpsilonTransition AUTOMATA_CONCATENATION_EPSILON_TRANSITION;
+};
+
+} /* namespace transform */
+
+} /* namespace automaton */
+
+#endif /* AUTOMATA_CONCATENATION_EPSILON_H_ */
diff --git a/alib2algo/src/automaton/transform/common/PairLabel.cpp b/alib2algo/src/automaton/transform/common/PairLabel.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..76b42064854024030afd54da44d2e7ce6bbc3198
--- /dev/null
+++ b/alib2algo/src/automaton/transform/common/PairLabel.cpp
@@ -0,0 +1,26 @@
+/*
+ * PairLabel.cpp
+ *
+ *  Created on: 20. 11. 2014
+ *	  Author: Tomas Pecka
+ */
+
+#include "PairLabel.h"
+
+namespace automaton {
+
+namespace transform {
+
+automaton::State pairLabel(const label::Label& first, const automaton::State& second)
+{
+	return automaton::State(label::Label(label::LabelPairLabel(std::make_pair(first, second.getName()))));
+}
+
+automaton::State pairLabel(const automaton::State& first, const automaton::State& second)
+{
+	return automaton::State(label::Label(label::LabelPairLabel(std::make_pair(first.getName(), second.getName()))));
+}
+
+} /* namespace transform */
+
+} /* namespace automaton */
diff --git a/alib2algo/src/automaton/transform/common/PairLabel.h b/alib2algo/src/automaton/transform/common/PairLabel.h
new file mode 100644
index 0000000000000000000000000000000000000000..ed2e334d54d622d61d5fbf995e14bd5d82e5740a
--- /dev/null
+++ b/alib2algo/src/automaton/transform/common/PairLabel.h
@@ -0,0 +1,25 @@
+/*
+ * PairLabel.h
+ *
+ *  Created on: 20. 11. 2014
+ *	  Author: Tomas Pecka
+ */
+
+#ifndef PAIR_LABEL_H_
+#define PAIR_LABEL_H_
+
+#include <automaton/common/State.h>
+#include <label/LabelPairLabel.h>
+
+namespace automaton {
+
+namespace transform {
+
+automaton::State pairLabel(const label::Label& first, const automaton::State& second);
+automaton::State pairLabel(const automaton::State& first, const automaton::State& second);
+
+} /* namespace transform */
+
+} /* namespace automaton */
+
+#endif /* PAIR_LABEL_H_ */