diff --git a/alangop2/makefile b/alangop2/makefile
new file mode 100644
index 0000000000000000000000000000000000000000..1e4036e680cc48c7e75cfb3686bd37c9f80a9628
--- /dev/null
+++ b/alangop2/makefile
@@ -0,0 +1,128 @@
+SHELL:=/bin/bash
+EXECUTABLE:=alangop2
+
+define NEW_LINE
+
+
+endef
+
+export NEW_LINE
+
+LDFLAGS_DEBUG:=-L../alib2data/lib-debug -L../alib2algo/lib-debug -rdynamic -lxml2 -lalib2data -lalib2algo -Wl,-rpath,.
+
+LDFLAGS_RELEASE:=-L../alib2data/lib-release -L../alib2algo/lib-release -rdynamic -lxml2 -lalib2data -lalib2algo -Wl,-rpath,.
+
+OBJECTS_DEBUG:=$(patsubst src/%.cpp, obj-debug/%.o, $(shell find src/ -name *cpp))
+
+OBJECTS_RELEASE:=$(patsubst src/%.cpp, obj-release/%.o, $(shell find src/ -name *cpp))
+
+.PHONY: all build-debug clean-debug doc
+
+all:
+	@echo "What to do master?"
+
+obj%/makefile: makefile
+	mkdir -p $(dir $@)
+	echo "\
+	SHELL:=/bin/bash$${NEW_LINE}\
+	SRCDIR:=$${NEW_LINE}\
+	DEPTH:=$${NEW_LINE}\
+	OBJECTS_BASE_DIR:=$${NEW_LINE}\
+	$${NEW_LINE}\
+	define NEW_LINE$${NEW_LINE}\
+	$${NEW_LINE}\
+	$${NEW_LINE}\
+	endef$${NEW_LINE}\
+	$${NEW_LINE}\
+	export NEW_LINE$${NEW_LINE}\
+	$${NEW_LINE}\
+	CXXFLAGS:= -std=c++11 \$$(CXX_OTHER_FLAGS) -c -Wall -pedantic -Wextra -fPIC -I/usr/include/libxml2/ -I../../\$$(DEPTH)alib2data/src/ -I../../\$$(DEPTH)alib2algo/src/$${NEW_LINE}\
+	$${NEW_LINE}\
+	SOURCES:= \$$(shell find ../\$$(DEPTH)\$$(SOURCES_BASE_DIR)/\$$(SRCDIR) -maxdepth 1 -type f -name \"*.cpp\")$${NEW_LINE}\
+	DEPENDENCIES:= \$$(patsubst ../\$$(DEPTH)\$$(SOURCES_BASE_DIR)/\$$(SRCDIR)%.cpp, ../\$$(DEPTH)\$$(OBJECTS_BASE_DIR)/\$$(SRCDIR)%.d, \$$(SOURCES))$${NEW_LINE}\
+	OBJECTS:= \$$(patsubst %.d, %.o, \$$(DEPENDENCIES))$${NEW_LINE}\
+	SOURCES_DIRS:= \$$(shell find ../\$$(DEPTH)\$$(SOURCES_BASE_DIR)/\$$(SRCDIR) -maxdepth 1 -mindepth 1 -type d)$${NEW_LINE}\
+	OBJECTS_DIRS:= \$$(patsubst ../\$$(DEPTH)\$$(SOURCES_BASE_DIR)/\$$(SRCDIR)%, %/, \$$(SOURCES_DIRS))$${NEW_LINE}\
+	OBJECTS_DIRS_MAKEFILES:= \$$(patsubst %, %makefile, \$$(OBJECTS_DIRS))$${NEW_LINE}\
+	$${NEW_LINE}\
+	.PHONY: all$${NEW_LINE}\
+	.PRECIOUS: \$$(DEPENDECIES) \$$(OBJECTS_DIRS_MAKEFILES)$${NEW_LINE}\
+	$${NEW_LINE}\
+	all: \$$(OBJECTS_DIRS) \$$(OBJECTS)$${NEW_LINE}\
+	$${NEW_LINE}\
+	%.d: makefile$${NEW_LINE}\
+		@echo \"\\$${NEW_LINE}\
+		\$$(shell sha1sum <<< \"\$$@\" | sed \"s/  -//g\") = \\$$\$$(shell (\\$$\$$(CXX) -MM \\$$\$$(CXXFLAGS) \$$(patsubst ../\$$(DEPTH)\$$(OBJECTS_BASE_DIR)/\$$(SRCDIR)%.d,../\$$(DEPTH)\$$(SOURCES_BASE_DIR)/\$$(SRCDIR)%.cpp, \$$@) 2>/dev/null || echo \\\"\$$(patsubst ../\$$(DEPTH)\$$(OBJECTS_BASE_DIR)/\$$(SRCDIR)%.d,../\$$(DEPTH)\$$(SOURCES_BASE_DIR)/\$$(SRCDIR)%.cpp, \$$@) FORCE\\\") | sed \\\"s/.*://g;s/\\\\\\\\\\\\\\\\//g\\\")\$$\$${NEW_LINE}\\$${NEW_LINE}\
+		\$$(patsubst %.d,%.o, \$$@): \\$$\$$(\$$(shell sha1sum <<< \"\$$@\" | sed \"s/  -//g\")) makefile\$$\$${NEW_LINE}\\$${NEW_LINE}\
+			\\$$\$$(CXX) \\$$\$$(CXXFLAGS) \\$$\$$< -o \$$(patsubst %.d,%.o, \$$@)\$$\$${NEW_LINE}\\$${NEW_LINE}\
+		\" > \$$@$${NEW_LINE}\
+	$${NEW_LINE}\
+	%/makefile: makefile$${NEW_LINE}\
+		mkdir -p \$$(dir \$$@)$${NEW_LINE}\
+		cp makefile \$$@$${NEW_LINE}\
+	$${NEW_LINE}\
+	%/: FORCE | %/makefile$${NEW_LINE}\
+		@accesstime=\`stat -c %Y \$$@\` && \\$${NEW_LINE}\
+		\$$(MAKE) -C \$$@ SRCDIR=\$$(SRCDIR)\$$(notdir \$$(patsubst %/, %, \$$@))/ DEPTH=\$$(DEPTH)../ OBJECTS_BASE_DIR=\$$(OBJECTS_BASE_DIR) SOURCES_BASE_DIR=\$$(SOURCES_BASE_DIR) CXX_OTHER_FLAGS=\"\$$(CXX_OTHER_FLAGS)\" && \\$${NEW_LINE}\
+		accesstime2=\`stat -c %Y \$$@\` && \\$${NEW_LINE}\
+		if [ "\$$\$$accesstime" -ne "\$$\$$accesstime2" ]; then \\$${NEW_LINE}\
+			touch .; \\$${NEW_LINE}\
+		fi$${NEW_LINE}\
+	$${NEW_LINE}\
+	FORCE:$${NEW_LINE}\
+	$${NEW_LINE}\
+	-include \$$(DEPENDENCIES)" > $@
+
+debug: build-debug
+
+release: build-release
+
+clean: clean-debug clean-release
+	$(RM) -r doc
+
+
+
+bin-debug/$(EXECUTABLE): obj-debug/ $(OBJECTS_DEBUG)
+	mkdir -p $(dir $@)
+	$(CXX) $(OBJECTS_DEBUG) -o $@ $(LDFLAGS_DEBUG)
+
+bin-release/$(EXECUTABLE): obj-release/ $(OBJECTS_RELEASE)
+	mkdir -p $(dir $@)
+	$(CXX) $(OBJECTS_RELEASE) -o $@ $(LDFLAGS_RELEASE)
+
+
+
+obj-debug/: FORCE | obj-debug/makefile
+	$(MAKE) -C $@ OBJECTS_BASE_DIR=obj-debug SOURCES_BASE_DIR=src CXX_OTHER_FLAGS="-g -O0"
+
+obj-release/: FORCE | obj-release/makefile
+	$(MAKE) -C $@ OBJECTS_BASE_DIR=obj-release SOURCES_BASE_DIR=src CXX_OTHER_FLAGS="-O3"
+
+
+
+$(OBJECTS_DEBUG): obj-debug/
+
+$(OBJECTS_RELEASE): obj-release/
+
+
+build-debug: bin-debug/$(EXECUTABLE)
+
+build-release: bin-release/$(EXECUTABLE)
+
+
+
+clean-debug:
+	$(RM) -r *.o *.d bin-debug obj-debug
+
+clean-release:
+	$(RM) -r *.o *.d bin-release obj-release
+
+
+
+FORCE:
+
+
+
+doc:
+	doxygen
+
diff --git a/alangop2/src/alangop.cpp b/alangop2/src/alangop.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7c3566564836974a9c886de5a46038865a334335
--- /dev/null
+++ b/alangop2/src/alangop.cpp
@@ -0,0 +1,114 @@
+/*
+ * alangop.cpp
+ *
+ *  Created on: 20. 11. 2014
+ *	  Author: Tomas Pecka
+ */
+
+#include <tclap/CmdLine.h>
+#include <vector>
+
+#include <factory/DataFactory.hpp>
+#include <exception/AlibException.h>
+#include <automaton/Automaton.h>
+
+#include <automaton/transform/AutomataConcatenationEpsilonTransition.h>
+#include <automaton/transform/AutomataConcatenation.h>
+#include <automaton/transform/AutomataIntersectionCartesianProduct.h>
+#include <automaton/transform/AutomataUnionCartesianProduct.h>
+#include <automaton/transform/AutomataUnionEpsilonTransition.h>
+#include <automaton/transform/AutomatonIterationEpsilonTransition.h>
+#include <automaton/transform/AutomatonIteration.h>
+
+
+int main(int argc, char* argv[]) {
+	try {
+		TCLAP::CmdLine cmd("Stringology algorithm access binary", ' ', "0.01");
+
+		std::vector<std::string> allowed;
+		allowed.push_back("unionEpsilon");
+		allowed.push_back("unionCartesian");
+		allowed.push_back("concatenationEpsilon");
+		allowed.push_back("concatenation");
+		allowed.push_back("intersectionCartesian");
+		allowed.push_back("iterationEpsilon");
+		allowed.push_back("iteration");
+		TCLAP::ValuesConstraint<std::string> allowedVals( allowed );
+
+		TCLAP::ValueArg<std::string> algorithm(	"a",	"algorithm",	"Execute algorithm", true,	"",	&allowedVals);
+		cmd.add(algorithm);
+
+		TCLAP::ValueArg<std::string> a1(	"i",	"input1",	"First automaton",	false,	"-",	"file");
+		TCLAP::ValueArg<std::string> a2(	"j",	"input2",	"Second automaton",	false,	"-",	"file");
+        cmd.add( a1 );
+        cmd.add( a2 );
+
+		cmd.parse(argc,argv);
+
+		std::list<sax::Token> a1Tokens, a2Tokens;
+		if(a1.isSet()) {
+			if(a1.getValue() == "-") {
+				sax::SaxParseInterface::parseStdin(a1Tokens);
+			} else {
+				sax::SaxParseInterface::parseFile(a1.getValue(), a1Tokens);
+			}
+		} else {
+			sax::SaxParseInterface::parseStdin(a1Tokens);
+		}
+
+		if(a2.isSet()) {
+			if(a2.getValue() == "-") {
+				sax::SaxParseInterface::parseStdin(a2Tokens);
+			} else {
+				sax::SaxParseInterface::parseFile(a2.getValue(), a2Tokens);
+			}
+		} else if(algorithm.getValue() != "iteration" && algorithm.getValue() != "iterationEpsilon") {
+			sax::SaxParseInterface::parseStdin(a2Tokens);
+		}
+
+		automaton::Automaton automaton1 = alib::DataFactory::fromTokens<automaton::Automaton>(a1Tokens);
+
+		if( algorithm.getValue() == "unionEpsilon") {
+			automaton::Automaton automaton2 = alib::DataFactory::fromTokens<automaton::Automaton>(a2Tokens);
+			alib::DataFactory::toStdout(automaton::transform::AutomataUnionEpsilonTransition::unification(automaton1, automaton2));
+			return 0;
+		} else if( algorithm.getValue() == "unionCartesian") {
+			automaton::Automaton automaton2 = alib::DataFactory::fromTokens<automaton::Automaton>(a2Tokens);
+			alib::DataFactory::toStdout(automaton::transform::AutomataUnionCartesianProduct::unification(automaton1, automaton2));
+			return 0;
+		} else if( algorithm.getValue() == "concatenationEpsilon") {
+			automaton::Automaton automaton2 = alib::DataFactory::fromTokens<automaton::Automaton>(a2Tokens);
+			alib::DataFactory::toStdout(automaton::transform::AutomataConcatenationEpsilonTransition::concatenation(automaton1, automaton2));
+			return 0;
+		} else if( algorithm.getValue() == "concatenation") {
+			automaton::Automaton automaton2 = alib::DataFactory::fromTokens<automaton::Automaton>(a2Tokens);
+			alib::DataFactory::toStdout(automaton::transform::AutomataConcatenation::concatenation(automaton1, automaton2));
+			return 0;
+		} else if( algorithm.getValue() == "intersectionCartesian") {
+			automaton::Automaton automaton2 = alib::DataFactory::fromTokens<automaton::Automaton>(a2Tokens);
+			alib::DataFactory::toStdout(automaton::transform::AutomataIntersectionCartesianProduct::intersection(automaton1, automaton2));
+			return 0;
+		} else if( algorithm.getValue() == "iteration") {
+			alib::DataFactory::toStdout(automaton::transform::AutomatonIteration::iteration(automaton1));
+			return 0;
+		} else if( algorithm.getValue() == "iterationEpsilon") {
+			alib::DataFactory::toStdout(automaton::transform::AutomatonIterationEpsilonTransition::iteration(automaton1));
+			return 0;
+		} else {
+			throw exception::AlibException( "Invalid algorithm" );
+			return 1;
+		}
+	} catch( const exception::AlibException & exception ) {
+		alib::DataFactory::toStdout( exception );
+		return 1;
+	} catch(const TCLAP::ArgException& exception) {
+		std::cout << exception.error() << std::endl;
+		return 2;
+	} catch (const std::exception& exception) {
+		std::cerr << "Exception caught: " << exception.what() << std::endl;
+		return 3;
+	} catch(...) {
+		std::cerr << "Unknown exception caught." << std::endl;
+		return 127;
+	}
+}
diff --git a/alib2algo/src/automaton/transform/AutomataConcatenation.cpp b/alib2algo/src/automaton/transform/AutomataConcatenation.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c8e3cdbec3fd115f56d6ef396ef795a97a75eca0
--- /dev/null
+++ b/alib2algo/src/automaton/transform/AutomataConcatenation.cpp
@@ -0,0 +1,207 @@
+/*
+ * AutomataConcatenation.cpp
+ *
+ *  Created on: 20. 11. 2014
+ *	  Author: Tomas Pecka
+ */
+
+#include "AutomataConcatenation.h"
+#include <exception/AlibException.h>
+#include <label/Label.h>
+#include "common/PairLabel.h"
+
+#define AUTOMATON_FIRST  (label::labelFrom(1))
+#define AUTOMATON_SECOND (label::labelFrom(2))
+
+namespace automaton
+{
+
+namespace transform
+{
+
+automaton::Automaton AutomataConcatenation::concatenation(const automaton::Automaton& first, const automaton::Automaton& second)
+{
+	AutomatonBase* out;
+	Accept((void*) &out, first.getData(), second.getData(), AutomataConcatenation::AUTOMATA_CONCATENATION);
+	automaton::Automaton res(*out);
+	delete out;
+	return res;
+}
+
+automaton::NFA AutomataConcatenation::concatenation(const automaton::NFA& first, const automaton::NFA& second)
+{
+	automaton::State q01q02(pairLabel(first.getInitialState(), second.getInitialState()));
+	automaton::NFA res(pairLabel(AUTOMATON_FIRST, first.getInitialState()));
+
+	for(const auto& q : first.getStates())
+		res.addState(pairLabel(AUTOMATON_FIRST, q));
+	for(const auto& q : second.getStates())
+		res.addState(pairLabel(AUTOMATON_SECOND, q));
+	res.addState(q01q02);
+
+	for(const auto& symbol : first.getInputAlphabet())
+		res.addInputSymbol(symbol);
+	for(const auto& symbol : second.getInputAlphabet())
+		res.addInputSymbol(symbol);
+
+	if(first.getFinalStates().count(first.getInitialState()) > 0)
+		res.setInitialState(q01q02);
+
+	for(const auto& t : first.getTransitions())
+	{
+		for(const auto& q : t.second)
+		{
+			res.addTransition(pairLabel(AUTOMATON_FIRST, t.first.first), t.first.second, pairLabel(AUTOMATON_FIRST, q));
+
+			if(first.getFinalStates().count(q) > 0)
+				res.addTransition(pairLabel(AUTOMATON_FIRST, t.first.first), t.first.second, pairLabel(AUTOMATON_SECOND, second.getInitialState()));
+		}
+	}
+	for(const auto& t : second.getTransitions())
+		for(const auto& q : t.second)
+			res.addTransition(pairLabel(AUTOMATON_SECOND, t.first.first), t.first.second, pairLabel(AUTOMATON_SECOND, q));
+
+	for(const auto& t : first.getTransitionsFromState(first.getInitialState()))
+		for(const auto& q : t.second)
+			res.addTransition(q01q02, t.first.second, pairLabel(AUTOMATON_FIRST, q));
+	for(const auto& t : second.getTransitionsFromState(second.getInitialState()))
+		for(const auto& q : t.second)
+			res.addTransition(q01q02, t.first.second, pairLabel(AUTOMATON_SECOND, q));
+
+	for(const auto& q : second.getFinalStates())
+		res.addFinalState(pairLabel(AUTOMATON_SECOND, q));
+	if(first.getFinalStates().count(first.getInitialState()) > 0)
+		res.addFinalState(q01q02);
+
+	return res;
+}
+
+automaton::NFA AutomataConcatenation::concatenation(const automaton::DFA& first, const automaton::DFA& second)
+{
+	automaton::State q01q02(pairLabel(first.getInitialState(), second.getInitialState()));
+	automaton::NFA res(pairLabel(AUTOMATON_FIRST, first.getInitialState()));
+
+	for(const auto& q : first.getStates())
+		res.addState(pairLabel(AUTOMATON_FIRST, q));
+	for(const auto& q : second.getStates())
+		res.addState(pairLabel(AUTOMATON_SECOND, q));
+	res.addState(q01q02);
+
+	for(const auto& symbol : first.getInputAlphabet())
+		res.addInputSymbol(symbol);
+	for(const auto& symbol : second.getInputAlphabet())
+		res.addInputSymbol(symbol);
+
+	if(first.getFinalStates().count(first.getInitialState()) > 0)
+		res.setInitialState(q01q02);
+
+	for(const auto& t : first.getTransitions())
+	{
+		res.addTransition(pairLabel(AUTOMATON_FIRST, t.first.first), t.first.second, pairLabel(AUTOMATON_FIRST, t.second));
+
+		if(first.getFinalStates().count(t.second) > 0)
+			res.addTransition(pairLabel(AUTOMATON_FIRST, t.first.first), t.first.second, pairLabel(AUTOMATON_SECOND, second.getInitialState()));
+	}
+	for(const auto& t : second.getTransitions())
+		res.addTransition(pairLabel(AUTOMATON_SECOND, t.first.first), t.first.second, pairLabel(AUTOMATON_SECOND, t.second));
+
+	for(const auto& t : first.getTransitionsFromState(first.getInitialState()))
+		res.addTransition(q01q02, t.first.second, pairLabel(AUTOMATON_FIRST, t.second));
+	for(const auto& t : second.getTransitionsFromState(second.getInitialState()))
+		res.addTransition(q01q02, t.first.second, pairLabel(AUTOMATON_SECOND, t.second));
+
+	for(const auto& q : second.getFinalStates())
+		res.addFinalState(pairLabel(AUTOMATON_SECOND, q));
+	if(first.getFinalStates().count(first.getInitialState()) > 0)
+		res.addFinalState(q01q02);
+
+	return res;
+}
+
+void AutomataConcatenation::Visit(void*, const automaton::EpsilonNFA&, const automaton::EpsilonNFA&) const
+{
+	throw exception::AlibException("Unsupported automaton type EpsilonNFA");
+}
+
+void AutomataConcatenation::Visit(void*, const automaton::CompactNFA&, const automaton::CompactNFA&) const
+{
+	throw exception::AlibException("Unsupported automaton type CompactNFA");
+}
+
+void AutomataConcatenation::Visit(void* data, const automaton::DFA& first, const automaton::DFA& second) const
+{
+	AutomatonBase* &ret = *(AutomatonBase**) data;
+	ret = std::move(AutomataConcatenation::concatenation(first, second)).plunder();
+}
+
+void AutomataConcatenation::Visit(void*, const automaton::ExtendedNFA&, const automaton::ExtendedNFA&) const
+{
+	throw exception::AlibException("Unsupported automaton type ExtendedNFA");
+}
+
+void AutomataConcatenation::Visit(void*, const automaton::MultiInitialStateNFA&, const automaton::MultiInitialStateNFA&) const
+{
+	throw exception::AlibException("Unsupported automaton type MultiInitialStateNFA");
+}
+
+void AutomataConcatenation::Visit(void* data, const automaton::NFA& first, const automaton::NFA& second) const
+{
+	AutomatonBase* &ret = *(AutomatonBase**) data;
+	ret = std::move(AutomataConcatenation::concatenation(first, second)).plunder();
+}
+
+void AutomataConcatenation::Visit(void*, const automaton::DPDA&, const automaton::DPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type DPDA");
+}
+
+void AutomataConcatenation::Visit(void*, const automaton::NPDA&, const automaton::NPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type NPDA");
+}
+
+void AutomataConcatenation::Visit(void*, const automaton::InputDrivenNPDA&, const automaton::InputDrivenNPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type InputDrivenNPDA");
+}
+
+void AutomataConcatenation::Visit(void*, const automaton::RealTimeHeightDeterministicDPDA&, const automaton::RealTimeHeightDeterministicDPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type RealTimeHeightDeterministicDPDA");
+}
+
+void AutomataConcatenation::Visit(void*, const automaton::RealTimeHeightDeterministicNPDA&, const automaton::RealTimeHeightDeterministicNPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type RealTimeHeightDeterministicNPDA");
+}
+
+void AutomataConcatenation::Visit(void*, const automaton::SinglePopNPDA&, const automaton::SinglePopNPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type SinglePopNPDA");
+}
+
+void AutomataConcatenation::Visit(void*, const automaton::SinglePopDPDA&, const automaton::SinglePopDPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type SinglePopDPDA");
+}
+
+void AutomataConcatenation::Visit(void*, const automaton::VisiblyPushdownDPDA&, const automaton::VisiblyPushdownDPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type VisiblyPushdownDPDA");
+}
+
+void AutomataConcatenation::Visit(void*, const automaton::VisiblyPushdownNPDA&, const automaton::VisiblyPushdownNPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type VisiblyPushdownNPDA");
+}
+
+void AutomataConcatenation::Visit(void*, const automaton::OneTapeDTM&, const automaton::OneTapeDTM&) const
+{
+	throw exception::AlibException("Unsupported automaton type OneTapeDTM");
+}
+
+const AutomataConcatenation AutomataConcatenation::AUTOMATA_CONCATENATION;
+
+} /* namespace transform */
+
+} /* namespace automaton */
diff --git a/alib2algo/src/automaton/transform/AutomataConcatenation.h b/alib2algo/src/automaton/transform/AutomataConcatenation.h
new file mode 100644
index 0000000000000000000000000000000000000000..9ee1b5523e20a2f73e0fc9a5a8f274420606425c
--- /dev/null
+++ b/alib2algo/src/automaton/transform/AutomataConcatenation.h
@@ -0,0 +1,59 @@
+/*
+ * AutomataConcatenation.h
+ *
+ *  Created on: 20. 11. 2014
+ *	  Author: Tomas Pecka
+ */
+
+#ifndef AUTOMATA_CONCATENATION_H_
+#define AUTOMATA_CONCATENATION_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) (Melichar, 2.78)
+ */
+class AutomataConcatenation : public automaton::VisitableAutomatonBase::const_promoting_visitor_type
+{
+public:
+	static automaton::Automaton concatenation(const automaton::Automaton& first, const automaton::Automaton& second);
+
+	static automaton::NFA concatenation(const automaton::DFA& first, const automaton::DFA& second);
+	static automaton::NFA concatenation(const automaton::NFA& first, const automaton::NFA& 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 AutomataConcatenation AUTOMATA_CONCATENATION;
+};
+
+} /* namespace transform */
+
+} /* namespace automaton */
+
+#endif /* AUTOMATA_CONCATENATION_H_ */
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/AutomataIntersectionCartesianProduct.cpp b/alib2algo/src/automaton/transform/AutomataIntersectionCartesianProduct.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a287a18b3ca2c412e1b9097ed1b9948d8eb65de9
--- /dev/null
+++ b/alib2algo/src/automaton/transform/AutomataIntersectionCartesianProduct.cpp
@@ -0,0 +1,182 @@
+/*
+ * AutomataIntersectionCartesianProduct.cpp
+ *
+ *  Created on: 20. 11. 2014
+ *	  Author: Tomas Pecka
+ */
+
+#include "AutomataIntersectionCartesianProduct.h"
+#include <exception/AlibException.h>
+#include "common/PairLabel.h"
+
+#define AUTOMATON_FIRST  1
+#define AUTOMATON_SECOND 2
+
+namespace automaton
+{
+
+namespace transform
+{
+
+automaton::Automaton AutomataIntersectionCartesianProduct::intersection(const automaton::Automaton& first, const automaton::Automaton& second)
+{
+	AutomatonBase* out;
+	Accept((void*) &out, first.getData(), second.getData(), AutomataIntersectionCartesianProduct::AUTOMATA_INTERSECTION_CARTESIAN_PRODUCT);
+	automaton::Automaton res(*out);
+	delete out;
+	return res;
+}
+
+automaton::DFA AutomataIntersectionCartesianProduct::intersection(const automaton::DFA& first, const automaton::DFA& second)
+{
+	automaton::State q0(pairLabel(first.getInitialState(), second.getInitialState()));
+	automaton::DFA res(q0);
+
+	for(const auto& a : first.getInputAlphabet())
+		res.addInputSymbol(a);
+	for(const auto& a : second.getInputAlphabet())
+		res.addInputSymbol(a);
+
+	for(const auto& p : first.getStates())
+		for(const auto& q : second.getStates())
+			res.addState(automaton::State(pairLabel(p, q)));
+
+	for(const auto& p : first.getFinalStates())
+		for(const auto& q : second.getFinalStates())
+			res.addFinalState(automaton::State(pairLabel(p, q)));
+
+	for(const auto& state : res.getStates())
+	{
+		const label::Label& label_p = static_cast<const label::LabelPairLabel&>(state.getName().getData()).getData().first;
+		const label::Label& label_q = static_cast<const label::LabelPairLabel&>(state.getName().getData()).getData().second;
+
+		for(const auto& tp : first.getTransitionsFromState(automaton::State(label_p)))
+			for(const auto& tq : second.getTransitionsFromState(automaton::State(label_q)))
+				if(tp.first.second == tq.first.second)
+					res.addTransition(state, tp.first.second, automaton::State(pairLabel(tp.second, tq.second)));
+	}
+
+	return res;
+}
+
+automaton::NFA AutomataIntersectionCartesianProduct::intersection(const automaton::NFA& first, const automaton::NFA& second)
+{
+	automaton::State q0(pairLabel(first.getInitialState(), second.getInitialState()));
+	automaton::NFA res(q0);
+
+	for(const auto& a : first.getInputAlphabet())
+		res.addInputSymbol(a);
+	for(const auto& a : second.getInputAlphabet())
+		res.addInputSymbol(a);
+
+	for(const auto& p : first.getStates())
+		for(const auto& q : second.getStates())
+			res.addState(automaton::State(pairLabel(p, q)));
+
+	for(const auto& p : first.getFinalStates())
+		for(const auto& q : second.getFinalStates())
+			res.addFinalState(automaton::State(pairLabel(p, q)));
+
+	for(const auto& state : res.getStates())
+	{
+		const label::Label& label_p = static_cast<const label::LabelPairLabel&>(state.getName().getData()).getData().first;
+		const label::Label& label_q = static_cast<const label::LabelPairLabel&>(state.getName().getData()).getData().second;
+
+		for(const auto& tp : first.getTransitionsFromState(automaton::State(label_p)))
+			for(const auto& tq : second.getTransitionsFromState(automaton::State(label_q)))
+				if(tp.first.second == tq.first.second)
+					for(const auto& p : tp.second)
+						for(const auto& q : tq.second)
+							res.addTransition(state, tp.first.second, automaton::State(pairLabel(p, q)));
+	}
+
+	return res;
+}
+
+void AutomataIntersectionCartesianProduct::Visit(void*, const automaton::EpsilonNFA&, const automaton::EpsilonNFA&) const
+{
+	throw exception::AlibException("Unsupported automaton type EpsilonNFA");
+}
+
+void AutomataIntersectionCartesianProduct::Visit(void*, const automaton::CompactNFA&, const automaton::CompactNFA&) const
+{
+	throw exception::AlibException("Unsupported automaton type CompactNFA");
+}
+
+void AutomataIntersectionCartesianProduct::Visit(void* data, const automaton::DFA& first, const automaton::DFA& second) const
+{
+	AutomatonBase* &ret = *(AutomatonBase**) data;
+	ret = std::move(AutomataIntersectionCartesianProduct::intersection(first, second)).plunder();
+}
+
+void AutomataIntersectionCartesianProduct::Visit(void*, const automaton::ExtendedNFA&, const automaton::ExtendedNFA&) const
+{
+	throw exception::AlibException("Unsupported automaton type ExtendedNFA");
+}
+
+void AutomataIntersectionCartesianProduct::Visit(void*, const automaton::MultiInitialStateNFA&, const automaton::MultiInitialStateNFA&) const
+{
+	throw exception::AlibException("Unsupported automaton type MultiInitialStateNFA");
+}
+
+void AutomataIntersectionCartesianProduct::Visit(void* data, const automaton::NFA& first, const automaton::NFA& second) const
+{
+	AutomatonBase* &ret = *(AutomatonBase**) data;
+	ret = std::move(AutomataIntersectionCartesianProduct::intersection(first, second)).plunder();
+}
+
+void AutomataIntersectionCartesianProduct::Visit(void*, const automaton::DPDA&, const automaton::DPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type DPDA");
+}
+
+void AutomataIntersectionCartesianProduct::Visit(void*, const automaton::NPDA&, const automaton::NPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type NPDA");
+}
+
+void AutomataIntersectionCartesianProduct::Visit(void*, const automaton::InputDrivenNPDA&, const automaton::InputDrivenNPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type InputDrivenNPDA");
+}
+
+void AutomataIntersectionCartesianProduct::Visit(void*, const automaton::RealTimeHeightDeterministicDPDA&, const automaton::RealTimeHeightDeterministicDPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type RealTimeHeightDeterministicDPDA");
+}
+
+void AutomataIntersectionCartesianProduct::Visit(void*, const automaton::RealTimeHeightDeterministicNPDA&, const automaton::RealTimeHeightDeterministicNPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type RealTimeHeightDeterministicNPDA");
+}
+
+void AutomataIntersectionCartesianProduct::Visit(void*, const automaton::SinglePopNPDA&, const automaton::SinglePopNPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type SinglePopNPDA");
+}
+
+void AutomataIntersectionCartesianProduct::Visit(void*, const automaton::SinglePopDPDA&, const automaton::SinglePopDPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type SinglePopDPDA");
+}
+
+void AutomataIntersectionCartesianProduct::Visit(void*, const automaton::VisiblyPushdownDPDA&, const automaton::VisiblyPushdownDPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type VisiblyPushdownDPDA");
+}
+
+void AutomataIntersectionCartesianProduct::Visit(void*, const automaton::VisiblyPushdownNPDA&, const automaton::VisiblyPushdownNPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type VisiblyPushdownNPDA");
+}
+
+void AutomataIntersectionCartesianProduct::Visit(void*, const automaton::OneTapeDTM&, const automaton::OneTapeDTM&) const
+{
+	throw exception::AlibException("Unsupported automaton type OneTapeDTM");
+}
+
+const AutomataIntersectionCartesianProduct AutomataIntersectionCartesianProduct::AUTOMATA_INTERSECTION_CARTESIAN_PRODUCT;
+
+} /* namespace transform */
+
+} /* namespace automaton */
diff --git a/alib2algo/src/automaton/transform/AutomataIntersectionCartesianProduct.h b/alib2algo/src/automaton/transform/AutomataIntersectionCartesianProduct.h
new file mode 100644
index 0000000000000000000000000000000000000000..2c00f01ea101221d630aa47c7e9578b4ff094c82
--- /dev/null
+++ b/alib2algo/src/automaton/transform/AutomataIntersectionCartesianProduct.h
@@ -0,0 +1,59 @@
+/*
+ * AutomataIntersectionCartesianProduct.h
+ *
+ *  Created on: 20. 11. 2014
+ *	  Author: Tomas Pecka
+ */
+
+#ifndef AUTOMATA_INTERSECTION_CARTESIAN_H_
+#define AUTOMATA_INTERSECTION_CARTESIAN_H_
+
+#include <automaton/Automaton.h>
+#include <automaton/FSM/EpsilonNFA.h>
+
+namespace automaton
+{
+
+namespace transform
+{
+
+/**
+ * Intersection of two automata.
+ *  - For finite automata A1, A2, we create automaton L accepting L(A1) \cap L(A2) (Melichar, 2.75)
+ */
+class AutomataIntersectionCartesianProduct : public automaton::VisitableAutomatonBase::const_promoting_visitor_type
+{
+public:
+	static automaton::Automaton intersection(const automaton::Automaton& first, const automaton::Automaton& second);
+
+	static automaton::NFA intersection(const automaton::NFA& first, const automaton::NFA& second);
+	static automaton::DFA intersection(const automaton::DFA& first, const automaton::DFA& 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 AutomataIntersectionCartesianProduct AUTOMATA_INTERSECTION_CARTESIAN_PRODUCT;
+};
+
+} /* namespace transform */
+
+} /* namespace automaton */
+
+#endif /* AUTOMATA_INTERSECTION_CARTESIAN_H_ */
diff --git a/alib2algo/src/automaton/transform/AutomataUnionCartesianProduct.cpp b/alib2algo/src/automaton/transform/AutomataUnionCartesianProduct.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2a03e1e1bf3bbe67d20e66ce2e2e5c338a903977
--- /dev/null
+++ b/alib2algo/src/automaton/transform/AutomataUnionCartesianProduct.cpp
@@ -0,0 +1,196 @@
+/*
+ * AutomataUnionCartesianProduct.cpp
+ *
+ *  Created on: 20. 11. 2014
+ *	  Author: Tomas Pecka
+ */
+
+#include "AutomataUnionCartesianProduct.h"
+#include <exception/AlibException.h>
+#include "common/PairLabel.h"
+
+#define AUTOMATON_FIRST  1
+#define AUTOMATON_SECOND 2
+
+namespace automaton
+{
+
+namespace transform
+{
+
+automaton::Automaton AutomataUnionCartesianProduct::unification(const automaton::Automaton& first, const automaton::Automaton& second)
+{
+	AutomatonBase* out;
+	Accept((void*) &out, first.getData(), second.getData(), AutomataUnionCartesianProduct::AUTOMATA_UNION_CARTESIAN_PRODUCT);
+	automaton::Automaton res(*out);
+	delete out;
+	return res;
+}
+
+automaton::DFA AutomataUnionCartesianProduct::unification(const automaton::DFA& first, const automaton::DFA& second)
+{
+	if(!first.isTotal() || !second.isTotal())
+		throw exception::AlibException("Automata must be total to unify with cartesian product");
+
+	automaton::State q0(pairLabel(first.getInitialState(), second.getInitialState()));
+	automaton::DFA res(q0);
+
+	for(const auto& a : first.getInputAlphabet())
+		res.addInputSymbol(a);
+	for(const auto& a : second.getInputAlphabet())
+		res.addInputSymbol(a);
+
+	for(const auto& p : first.getStates())
+		for(const auto& q : second.getStates())
+			res.addState(automaton::State(pairLabel(p, q)));
+
+	for(const auto& p : first.getFinalStates())
+		for(const auto& q : second.getStates())
+			res.addFinalState(automaton::State(pairLabel(p, q)));
+
+	for(const auto& p : first.getStates())
+		for(const auto& q : second.getFinalStates())
+			res.addFinalState(automaton::State(pairLabel(p, q)));
+
+	for(const auto& state : res.getStates())
+	{
+		const label::Label& label_p = static_cast<const label::LabelPairLabel&>(state.getName().getData()).getData().first;
+		const label::Label& label_q = static_cast<const label::LabelPairLabel&>(state.getName().getData()).getData().second;
+
+		for(const auto& tp : first.getTransitionsFromState(automaton::State(label_p)))
+			for(const auto& tq : second.getTransitionsFromState(automaton::State(label_q)))
+				if(tp.first.second == tq.first.second)
+					res.addTransition(state, tp.first.second, automaton::State(pairLabel(tp.second, tq.second)));
+	}
+
+	return res;
+}
+
+automaton::NFA AutomataUnionCartesianProduct::unification(const automaton::NFA& first, const automaton::NFA& second)
+{
+	if(!first.isTotal() || !second.isTotal())
+		throw exception::AlibException("Automata must be total to unify with cartesian product");
+
+	automaton::State q0(pairLabel(first.getInitialState(), second.getInitialState()));
+	automaton::NFA res(q0);
+
+	for(const auto& a : first.getInputAlphabet())
+		res.addInputSymbol(a);
+	for(const auto& a : second.getInputAlphabet())
+		res.addInputSymbol(a);
+
+	for(const auto& p : first.getStates())
+		for(const auto& q : second.getStates())
+			res.addState(automaton::State(pairLabel(p, q)));
+
+	for(const auto& p : first.getFinalStates())
+		for(const auto& q : second.getStates())
+			res.addFinalState(automaton::State(pairLabel(p, q)));
+
+	for(const auto& p : first.getStates())
+		for(const auto& q : second.getFinalStates())
+			res.addFinalState(automaton::State(pairLabel(p, q)));
+
+	for(const auto& state : res.getStates())
+	{
+		const label::Label& label_p = static_cast<const label::LabelPairLabel&>(state.getName().getData()).getData().first;
+		const label::Label& label_q = static_cast<const label::LabelPairLabel&>(state.getName().getData()).getData().second;
+
+		for(const auto& tp : first.getTransitionsFromState(automaton::State(label_p)))
+			for(const auto& tq : second.getTransitionsFromState(automaton::State(label_q)))
+				if(tp.first.second == tq.first.second)
+					for(const auto& p : tp.second)
+						for(const auto& q : tq.second)
+							res.addTransition(state, tp.first.second, automaton::State(pairLabel(p, q)));
+	}
+
+	return res;
+}
+
+void AutomataUnionCartesianProduct::Visit(void*, const automaton::EpsilonNFA&, const automaton::EpsilonNFA&) const
+{
+	throw exception::AlibException("Unsupported automaton type EpsilonNFA");
+}
+
+void AutomataUnionCartesianProduct::Visit(void*, const automaton::CompactNFA&, const automaton::CompactNFA&) const
+{
+	throw exception::AlibException("Unsupported automaton type CompactNFA");
+}
+
+void AutomataUnionCartesianProduct::Visit(void* data, const automaton::DFA& first, const automaton::DFA& second) const
+{
+	AutomatonBase* &ret = *(AutomatonBase**) data;
+	ret = std::move(AutomataUnionCartesianProduct::unification(first, second)).plunder();
+}
+
+void AutomataUnionCartesianProduct::Visit(void*, const automaton::ExtendedNFA&, const automaton::ExtendedNFA&) const
+{
+	throw exception::AlibException("Unsupported automaton type ExtendedNFA");
+}
+
+void AutomataUnionCartesianProduct::Visit(void*, const automaton::MultiInitialStateNFA&, const automaton::MultiInitialStateNFA&) const
+{
+	throw exception::AlibException("Unsupported automaton type MultiInitialStateNFA");
+}
+
+void AutomataUnionCartesianProduct::Visit(void* data, const automaton::NFA& first, const automaton::NFA& second) const
+{
+	AutomatonBase* &ret = *(AutomatonBase**) data;
+	ret = std::move(AutomataUnionCartesianProduct::unification(first, second)).plunder();
+}
+
+void AutomataUnionCartesianProduct::Visit(void*, const automaton::DPDA&, const automaton::DPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type DPDA");
+}
+
+void AutomataUnionCartesianProduct::Visit(void*, const automaton::NPDA&, const automaton::NPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type NPDA");
+}
+
+void AutomataUnionCartesianProduct::Visit(void*, const automaton::InputDrivenNPDA&, const automaton::InputDrivenNPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type InputDrivenNPDA");
+}
+
+void AutomataUnionCartesianProduct::Visit(void*, const automaton::RealTimeHeightDeterministicDPDA&, const automaton::RealTimeHeightDeterministicDPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type RealTimeHeightDeterministicDPDA");
+}
+
+void AutomataUnionCartesianProduct::Visit(void*, const automaton::RealTimeHeightDeterministicNPDA&, const automaton::RealTimeHeightDeterministicNPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type RealTimeHeightDeterministicNPDA");
+}
+
+void AutomataUnionCartesianProduct::Visit(void*, const automaton::SinglePopNPDA&, const automaton::SinglePopNPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type SinglePopNPDA");
+}
+
+void AutomataUnionCartesianProduct::Visit(void*, const automaton::SinglePopDPDA&, const automaton::SinglePopDPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type SinglePopDPDA");
+}
+
+void AutomataUnionCartesianProduct::Visit(void*, const automaton::VisiblyPushdownDPDA&, const automaton::VisiblyPushdownDPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type VisiblyPushdownDPDA");
+}
+
+void AutomataUnionCartesianProduct::Visit(void*, const automaton::VisiblyPushdownNPDA&, const automaton::VisiblyPushdownNPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type VisiblyPushdownNPDA");
+}
+
+void AutomataUnionCartesianProduct::Visit(void*, const automaton::OneTapeDTM&, const automaton::OneTapeDTM&) const
+{
+	throw exception::AlibException("Unsupported automaton type OneTapeDTM");
+}
+
+const AutomataUnionCartesianProduct AutomataUnionCartesianProduct::AUTOMATA_UNION_CARTESIAN_PRODUCT;
+
+} /* namespace transform */
+
+} /* namespace automaton */
diff --git a/alib2algo/src/automaton/transform/AutomataUnionCartesianProduct.h b/alib2algo/src/automaton/transform/AutomataUnionCartesianProduct.h
new file mode 100644
index 0000000000000000000000000000000000000000..305efabc5f7f082bcf9b98a4d322579f33181af3
--- /dev/null
+++ b/alib2algo/src/automaton/transform/AutomataUnionCartesianProduct.h
@@ -0,0 +1,59 @@
+/*
+ * AutomataUnionCartesianProduct.h
+ *
+ *  Created on: 20. 11. 2014
+ *	  Author: Tomas Pecka
+ */
+
+#ifndef AUTOMATA_UNION_CARTESIAN_H_
+#define AUTOMATA_UNION_CARTESIAN_H_
+
+#include <automaton/Automaton.h>
+#include <automaton/FSM/EpsilonNFA.h>
+
+namespace automaton
+{
+
+namespace transform
+{
+
+/**
+ * Union two automata.
+ *  - For finite automata A1, A2, we create automaton L accepting L(A1) \cup L(A2) (Melichar, 2.71)
+ */
+class AutomataUnionCartesianProduct : public automaton::VisitableAutomatonBase::const_promoting_visitor_type
+{
+public:
+	static automaton::Automaton unification(const automaton::Automaton& first, const automaton::Automaton& second);
+
+	static automaton::NFA unification(const automaton::NFA& first, const automaton::NFA& second);
+	static automaton::DFA unification(const automaton::DFA& first, const automaton::DFA& 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 AutomataUnionCartesianProduct AUTOMATA_UNION_CARTESIAN_PRODUCT;
+};
+
+} /* namespace transform */
+
+} /* namespace automaton */
+
+#endif /* AUTOMATA_UNION_CARTESIAN_H_ */
diff --git a/alib2algo/src/automaton/transform/AutomataUnionEpsilonTransition.cpp b/alib2algo/src/automaton/transform/AutomataUnionEpsilonTransition.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..848135ec651125cbf79921a4f98ee7ef0069b726
--- /dev/null
+++ b/alib2algo/src/automaton/transform/AutomataUnionEpsilonTransition.cpp
@@ -0,0 +1,229 @@
+/*
+ * AutomataUnionEpsilonTransition.cpp
+ *
+ *  Created on: 20. 11. 2014
+ *	  Author: Tomas Pecka
+ */
+
+#include "AutomataUnionEpsilonTransition.h"
+#include <exception/AlibException.h>
+#include "common/PairLabel.h"
+
+#define AUTOMATON_FIRST  1
+#define AUTOMATON_SECOND 2
+
+namespace automaton
+{
+
+namespace transform
+{
+
+automaton::Automaton AutomataUnionEpsilonTransition::unification(const automaton::Automaton& first, const automaton::Automaton& second)
+{
+	AutomatonBase* out;
+	Accept((void*) &out, first.getData(), second.getData(), AutomataUnionEpsilonTransition::AUTOMATA_UNION_EPSILON_TRANSITION);
+	automaton::Automaton res(*out);
+	delete out;
+	return res;
+}
+
+automaton::EpsilonNFA AutomataUnionEpsilonTransition::unification(const automaton::EpsilonNFA& first, const automaton::EpsilonNFA& second)
+{
+	std::set<automaton::State> states;
+	for(const auto& q : first.getStates())
+		states.insert(pairLabel(label::labelFrom(AUTOMATON_FIRST), q));
+	for(const auto& q : second.getStates())
+		states.insert(pairLabel(label::labelFrom(AUTOMATON_SECOND), q));
+
+	automaton::State q0 = automaton::createUniqueState(automaton::State("q0"), states);
+	automaton::EpsilonNFA res(q0);
+
+	for(const auto& a : first.getInputAlphabet())
+		res.addInputSymbol(a);
+	for(const auto& a : second.getInputAlphabet())
+		res.addInputSymbol(a);
+
+	for(const auto& q : states)
+		res.addState(q);
+
+	for(const auto& q : first.getFinalStates())
+		res.addFinalState(pairLabel(label::labelFrom(AUTOMATON_FIRST), q));
+	for(const auto& q : second.getFinalStates())
+		res.addFinalState(pairLabel(label::labelFrom(AUTOMATON_SECOND), q));
+
+	res.addTransition(q0, pairLabel(label::labelFrom(AUTOMATON_FIRST), first.getInitialState()));
+	res.addTransition(q0, pairLabel(label::labelFrom(AUTOMATON_SECOND), second.getInitialState()));
+
+	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));
+
+	return res;
+}
+
+automaton::EpsilonNFA AutomataUnionEpsilonTransition::unification(const automaton::NFA& first, const automaton::NFA& second)
+{
+	std::set<automaton::State> states;
+	for(const auto& q : first.getStates())
+		states.insert(pairLabel(label::labelFrom(AUTOMATON_FIRST), q));
+	for(const auto& q : second.getStates())
+		states.insert(pairLabel(label::labelFrom(AUTOMATON_SECOND), q));
+
+	automaton::State q0 = automaton::createUniqueState(automaton::State("q0"), states);
+	automaton::EpsilonNFA res(q0);
+
+	for(const auto& a : first.getInputAlphabet())
+		res.addInputSymbol(a);
+	for(const auto& a : second.getInputAlphabet())
+		res.addInputSymbol(a);
+
+	for(const auto& q : states)
+		res.addState(q);
+
+	for(const auto& q : first.getFinalStates())
+		res.addFinalState(pairLabel(label::labelFrom(AUTOMATON_FIRST), q));
+	for(const auto& q : second.getFinalStates())
+		res.addFinalState(pairLabel(label::labelFrom(AUTOMATON_SECOND), q));
+
+	res.addTransition(q0, pairLabel(label::labelFrom(AUTOMATON_FIRST), first.getInitialState()));
+	res.addTransition(q0, pairLabel(label::labelFrom(AUTOMATON_SECOND), second.getInitialState()));
+
+	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));
+
+	return res;
+}
+
+automaton::EpsilonNFA AutomataUnionEpsilonTransition::unification(const automaton::DFA& first, const automaton::DFA& second)
+{
+	std::set<automaton::State> states;
+	for(const auto& q : first.getStates())
+		states.insert(pairLabel(label::labelFrom(AUTOMATON_FIRST), q));
+	for(const auto& q : second.getStates())
+		states.insert(pairLabel(label::labelFrom(AUTOMATON_SECOND), q));
+
+	automaton::State q0 = automaton::createUniqueState(automaton::State("q0"), states);
+	automaton::EpsilonNFA res(q0);
+
+	for(const auto& a : first.getInputAlphabet())
+		res.addInputSymbol(a);
+	for(const auto& a : second.getInputAlphabet())
+		res.addInputSymbol(a);
+
+	for(const auto& q : states)
+		res.addState(q);
+
+	for(const auto& q : first.getFinalStates())
+		res.addFinalState(pairLabel(label::labelFrom(AUTOMATON_FIRST), q));
+	for(const auto& q : second.getFinalStates())
+		res.addFinalState(pairLabel(label::labelFrom(AUTOMATON_SECOND), q));
+
+	res.addTransition(q0, pairLabel(label::labelFrom(AUTOMATON_FIRST), first.getInitialState()));
+	res.addTransition(q0, pairLabel(label::labelFrom(AUTOMATON_SECOND), second.getInitialState()));
+
+	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));
+
+	return res;
+}
+
+void AutomataUnionEpsilonTransition::Visit(void* data, const automaton::EpsilonNFA& first, const automaton::EpsilonNFA& second) const
+{
+	AutomatonBase* &ret = *(AutomatonBase**) data;
+	ret = std::move(AutomataUnionEpsilonTransition::unification(first, second)).plunder();
+}
+
+void AutomataUnionEpsilonTransition::Visit(void*, const automaton::CompactNFA&, const automaton::CompactNFA&) const
+{
+	throw exception::AlibException("Unsupported automaton type CompactNFA");
+}
+
+void AutomataUnionEpsilonTransition::Visit(void* data, const automaton::DFA& first, const automaton::DFA& second) const
+{
+	AutomatonBase* &ret = *(AutomatonBase**) data;
+	ret = std::move(AutomataUnionEpsilonTransition::unification(first, second)).plunder();
+}
+
+void AutomataUnionEpsilonTransition::Visit(void*, const automaton::ExtendedNFA&, const automaton::ExtendedNFA&) const
+{
+	throw exception::AlibException("Unsupported automaton type ExtendedNFA");
+}
+
+void AutomataUnionEpsilonTransition::Visit(void*, const automaton::MultiInitialStateNFA&, const automaton::MultiInitialStateNFA&) const
+{
+	throw exception::AlibException("Unsupported automaton type MultiInitialStateNFA");
+}
+
+void AutomataUnionEpsilonTransition::Visit(void* data, const automaton::NFA& first, const automaton::NFA& second) const
+{
+	AutomatonBase* &ret = *(AutomatonBase**) data;
+	ret = std::move(AutomataUnionEpsilonTransition::unification(first, second)).plunder();
+}
+
+void AutomataUnionEpsilonTransition::Visit(void*, const automaton::DPDA&, const automaton::DPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type DPDA");
+}
+
+void AutomataUnionEpsilonTransition::Visit(void*, const automaton::NPDA&, const automaton::NPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type NPDA");
+}
+
+void AutomataUnionEpsilonTransition::Visit(void*, const automaton::InputDrivenNPDA&, const automaton::InputDrivenNPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type InputDrivenNPDA");
+}
+
+void AutomataUnionEpsilonTransition::Visit(void*, const automaton::RealTimeHeightDeterministicDPDA&, const automaton::RealTimeHeightDeterministicDPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type RealTimeHeightDeterministicDPDA");
+}
+
+void AutomataUnionEpsilonTransition::Visit(void*, const automaton::RealTimeHeightDeterministicNPDA&, const automaton::RealTimeHeightDeterministicNPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type RealTimeHeightDeterministicNPDA");
+}
+
+void AutomataUnionEpsilonTransition::Visit(void*, const automaton::SinglePopNPDA&, const automaton::SinglePopNPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type SinglePopNPDA");
+}
+
+void AutomataUnionEpsilonTransition::Visit(void*, const automaton::SinglePopDPDA&, const automaton::SinglePopDPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type SinglePopDPDA");
+}
+
+void AutomataUnionEpsilonTransition::Visit(void*, const automaton::VisiblyPushdownDPDA&, const automaton::VisiblyPushdownDPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type VisiblyPushdownDPDA");
+}
+
+void AutomataUnionEpsilonTransition::Visit(void*, const automaton::VisiblyPushdownNPDA&, const automaton::VisiblyPushdownNPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type VisiblyPushdownNPDA");
+}
+
+void AutomataUnionEpsilonTransition::Visit(void*, const automaton::OneTapeDTM&, const automaton::OneTapeDTM&) const
+{
+	throw exception::AlibException("Unsupported automaton type OneTapeDTM");
+}
+
+const AutomataUnionEpsilonTransition AutomataUnionEpsilonTransition::AUTOMATA_UNION_EPSILON_TRANSITION;
+
+} /* namespace transform */
+
+} /* namespace automaton */
diff --git a/alib2algo/src/automaton/transform/AutomataUnionEpsilonTransition.h b/alib2algo/src/automaton/transform/AutomataUnionEpsilonTransition.h
new file mode 100644
index 0000000000000000000000000000000000000000..855fc5b94d9b4ffa067785c0b14a1f87a9a5a2a7
--- /dev/null
+++ b/alib2algo/src/automaton/transform/AutomataUnionEpsilonTransition.h
@@ -0,0 +1,60 @@
+/*
+ * AutomataUnionEpsilonTransition.h
+ *
+ *  Created on: 20. 11. 2014
+ *	  Author: Tomas Pecka
+ */
+
+#ifndef AUTOMATA_UNION_EPSILON_H_
+#define AUTOMATA_UNION_EPSILON_H_
+
+#include <automaton/Automaton.h>
+#include <automaton/FSM/EpsilonNFA.h>
+
+namespace automaton
+{
+
+namespace transform
+{
+
+/**
+ * Union two automata.
+ *  - For finite automata A1, A2, we create automaton L accepting L(A1) \cup L(A2)
+ */
+class AutomataUnionEpsilonTransition : public automaton::VisitableAutomatonBase::const_promoting_visitor_type
+{
+public:
+	static automaton::Automaton unification(const automaton::Automaton& first, const automaton::Automaton& second);
+
+	static automaton::EpsilonNFA unification(const automaton::EpsilonNFA& first, const automaton::EpsilonNFA& second);
+	static automaton::EpsilonNFA unification(const automaton::NFA& first, const automaton::NFA& second);
+	static automaton::EpsilonNFA unification(const automaton::DFA& first, const automaton::DFA& 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 AutomataUnionEpsilonTransition AUTOMATA_UNION_EPSILON_TRANSITION;
+};
+
+} /* namespace transform */
+
+} /* namespace automaton */
+
+#endif /* AUTOMATA_UNION_EPSILON_H_ */
diff --git a/alib2algo/src/automaton/transform/AutomatonIteration.cpp b/alib2algo/src/automaton/transform/AutomatonIteration.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ad8dc7d4f1723d73db0d99f354c16d712a938296
--- /dev/null
+++ b/alib2algo/src/automaton/transform/AutomatonIteration.cpp
@@ -0,0 +1,125 @@
+/*
+ * AutomatonIteration.cpp
+ *
+ *  Created on: 29. 11. 2014
+ *	  Author: Tomas Pecka
+ */
+
+#include "AutomatonIteration.h"
+#include <exception/AlibException.h>
+
+namespace automaton
+{
+
+namespace transform
+{
+
+automaton::Automaton AutomatonIteration::iteration(const automaton::Automaton& automaton)
+{
+	AutomatonBase* out;
+	automaton.getData().Accept((void*) &out, AutomatonIteration::AUTOMATON_ITERATION);
+	automaton::Automaton res(*out);
+	delete out;
+	return res;
+}
+
+template<class T>
+automaton::NFA AutomatonIteration::iteration(const T& automaton)
+{
+	automaton::NFA res(automaton);
+
+	for(const auto& qf : res.getFinalStates())
+		for(const auto& t : res.getTransitionsToState(qf))
+			res.addTransition(t.first.first, t.first.second, res.getInitialState());
+
+	res.addFinalState(automaton.getInitialState());
+	return res;
+}
+
+void AutomatonIteration::Visit(void*, const automaton::EpsilonNFA&) const
+{
+	throw exception::AlibException("Unsupported automaton type EpsilonNFA");
+}
+
+void AutomatonIteration::Visit(void*, const automaton::CompactNFA&) const
+{
+	throw exception::AlibException("Unsupported automaton type CompactNFA");
+}
+
+void AutomatonIteration::Visit(void* data, const automaton::DFA& automaton) const
+{
+	AutomatonBase* &ret = *(AutomatonBase**) data;
+	ret = std::move(AutomatonIteration::iteration(automaton)).plunder();
+}
+
+void AutomatonIteration::Visit(void*, const automaton::ExtendedNFA&) const
+{
+	throw exception::AlibException("Unsupported automaton type ExtendedNFA");
+}
+
+void AutomatonIteration::Visit(void*, const automaton::MultiInitialStateNFA&) const
+{
+	throw exception::AlibException("Unsupported automaton type MultiInitialStateNFA");
+}
+
+void AutomatonIteration::Visit(void* data, const automaton::NFA& automaton) const
+{
+	AutomatonBase* &ret = *(AutomatonBase**) data;
+	ret = std::move(AutomatonIteration::iteration(automaton)).plunder();
+}
+
+void AutomatonIteration::Visit(void*, const automaton::DPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type DPDA");
+}
+
+void AutomatonIteration::Visit(void*, const automaton::NPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type NPDA");
+}
+
+void AutomatonIteration::Visit(void*, const automaton::InputDrivenNPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type InputDrivenNPDA");
+}
+
+void AutomatonIteration::Visit(void*, const automaton::RealTimeHeightDeterministicDPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type RealTimeHeightDeterministicDPDA");
+}
+
+void AutomatonIteration::Visit(void*, const automaton::RealTimeHeightDeterministicNPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type RealTimeHeightDeterministicNPDA");
+}
+
+void AutomatonIteration::Visit(void*, const automaton::SinglePopNPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type SinglePopNPDA");
+}
+
+void AutomatonIteration::Visit(void*, const automaton::SinglePopDPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type SinglePopDPDA");
+}
+
+void AutomatonIteration::Visit(void*, const automaton::VisiblyPushdownDPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type VisiblyPushdownDPDA");
+}
+
+void AutomatonIteration::Visit(void*, const automaton::VisiblyPushdownNPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type VisiblyPushdownNPDA");
+}
+
+void AutomatonIteration::Visit(void*, const automaton::OneTapeDTM&) const
+{
+	throw exception::AlibException("Unsupported automaton type OneTapeDTM");
+}
+
+const AutomatonIteration AutomatonIteration::AUTOMATON_ITERATION;
+
+} /* namespace transform */
+
+} /* namespace automaton */
diff --git a/alib2algo/src/automaton/transform/AutomatonIteration.h b/alib2algo/src/automaton/transform/AutomatonIteration.h
new file mode 100644
index 0000000000000000000000000000000000000000..e19861e088ecc80d8944f4325493a13c7d766b31
--- /dev/null
+++ b/alib2algo/src/automaton/transform/AutomatonIteration.h
@@ -0,0 +1,59 @@
+/*
+ * AutomatonIteration.h
+ *
+ *  Created on: 29. 11. 2014
+ *	  Author: Tomas Pecka
+ */
+
+#ifndef AUTOMATON_ITERATION_H_
+#define AUTOMATON_ITERATION_H_
+
+#include <automaton/Automaton.h>
+#include <automaton/FSM/EpsilonNFA.h>
+
+namespace automaton
+{
+
+namespace transform
+{
+
+/**
+ * Iterates language given by automaton
+ *  - For finite automaton A1, we create automaton L accepting L(A1)*
+ */
+class AutomatonIteration : public automaton::VisitableAutomatonBase::const_visitor_type
+{
+public:
+	static automaton::Automaton iteration(const automaton::Automaton& automaton);
+
+	template<class T>
+	static automaton::NFA iteration(const T& automaton);
+
+private:
+	void Visit(void* data, const automaton::CompactNFA& automaton) const;
+	void Visit(void* data, const automaton::DFA& automaton) const;
+	void Visit(void* data, const automaton::EpsilonNFA& automaton) const;
+	void Visit(void* data, const automaton::ExtendedNFA& automaton) const;
+	void Visit(void* data, const automaton::MultiInitialStateNFA& automaton) const;
+	void Visit(void* data, const automaton::NFA& automaton) const;
+
+	void Visit(void* data, const automaton::DPDA& automaton) const;
+	void Visit(void* data, const automaton::NPDA& automaton) const;
+	void Visit(void* data, const automaton::InputDrivenNPDA& automaton) const;
+	void Visit(void* data, const automaton::RealTimeHeightDeterministicDPDA& automaton) const;
+	void Visit(void* data, const automaton::RealTimeHeightDeterministicNPDA& automaton) const;
+	void Visit(void* data, const automaton::SinglePopNPDA& automaton) const;
+	void Visit(void* data, const automaton::SinglePopDPDA& automaton) const;
+	void Visit(void* data, const automaton::VisiblyPushdownDPDA& automaton) const;
+	void Visit(void* data, const automaton::VisiblyPushdownNPDA& automaton) const;
+
+	void Visit(void* data, const automaton::OneTapeDTM& automaton) const;
+
+	static const AutomatonIteration AUTOMATON_ITERATION;
+};
+
+} /* namespace transform */
+
+} /* namespace automaton */
+
+#endif /* AUTOMATON_ITERATION_H_ */
diff --git a/alib2algo/src/automaton/transform/AutomatonIterationEpsilonTransition.cpp b/alib2algo/src/automaton/transform/AutomatonIterationEpsilonTransition.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6cf3b6a1a90dbc7b53eb5449dca7054eef524ba2
--- /dev/null
+++ b/alib2algo/src/automaton/transform/AutomatonIterationEpsilonTransition.cpp
@@ -0,0 +1,125 @@
+/*
+ * AutomatonIterationEpsilonTransition.cpp
+ *
+ *  Created on: 20. 11. 2014
+ *	  Author: Tomas Pecka
+ */
+
+#include "AutomatonIterationEpsilonTransition.h"
+#include <exception/AlibException.h>
+
+namespace automaton
+{
+
+namespace transform
+{
+
+automaton::Automaton AutomatonIterationEpsilonTransition::iteration(const automaton::Automaton& automaton)
+{
+	AutomatonBase* out;
+	automaton.getData().Accept((void*) &out, AutomatonIterationEpsilonTransition::AUTOMATON_ITERATION_EPSILON_TRANSITION);
+	automaton::Automaton res(*out);
+	delete out;
+	return res;
+}
+
+template<class T>
+automaton::EpsilonNFA AutomatonIterationEpsilonTransition::iteration(const T& automaton)
+{
+	automaton::EpsilonNFA res(automaton);
+
+	for(const auto&q : automaton.getFinalStates())
+		res.addTransition(q, automaton.getInitialState());
+
+	res.addFinalState(automaton.getInitialState());
+	return res;
+}
+
+void AutomatonIterationEpsilonTransition::Visit(void* data, const automaton::EpsilonNFA& automaton) const
+{
+	AutomatonBase* &ret = *(AutomatonBase**) data;
+	ret = std::move(AutomatonIterationEpsilonTransition::iteration(automaton)).plunder();
+}
+
+void AutomatonIterationEpsilonTransition::Visit(void*, const automaton::CompactNFA&) const
+{
+	throw exception::AlibException("Unsupported automaton type CompactNFA");
+}
+
+void AutomatonIterationEpsilonTransition::Visit(void* data, const automaton::DFA& automaton) const
+{
+	AutomatonBase* &ret = *(AutomatonBase**) data;
+	ret = std::move(AutomatonIterationEpsilonTransition::iteration(automaton)).plunder();
+}
+
+void AutomatonIterationEpsilonTransition::Visit(void*, const automaton::ExtendedNFA&) const
+{
+	throw exception::AlibException("Unsupported automaton type ExtendedNFA");
+}
+
+void AutomatonIterationEpsilonTransition::Visit(void*, const automaton::MultiInitialStateNFA&) const
+{
+	throw exception::AlibException("Unsupported automaton type MultiInitialStateNFA");
+}
+
+void AutomatonIterationEpsilonTransition::Visit(void* data, const automaton::NFA& automaton) const
+{
+	AutomatonBase* &ret = *(AutomatonBase**) data;
+	ret = std::move(AutomatonIterationEpsilonTransition::iteration(automaton)).plunder();
+}
+
+void AutomatonIterationEpsilonTransition::Visit(void*, const automaton::DPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type DPDA");
+}
+
+void AutomatonIterationEpsilonTransition::Visit(void*, const automaton::NPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type NPDA");
+}
+
+void AutomatonIterationEpsilonTransition::Visit(void*, const automaton::InputDrivenNPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type InputDrivenNPDA");
+}
+
+void AutomatonIterationEpsilonTransition::Visit(void*, const automaton::RealTimeHeightDeterministicDPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type RealTimeHeightDeterministicDPDA");
+}
+
+void AutomatonIterationEpsilonTransition::Visit(void*, const automaton::RealTimeHeightDeterministicNPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type RealTimeHeightDeterministicNPDA");
+}
+
+void AutomatonIterationEpsilonTransition::Visit(void*, const automaton::SinglePopNPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type SinglePopNPDA");
+}
+
+void AutomatonIterationEpsilonTransition::Visit(void*, const automaton::SinglePopDPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type SinglePopDPDA");
+}
+
+void AutomatonIterationEpsilonTransition::Visit(void*, const automaton::VisiblyPushdownDPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type VisiblyPushdownDPDA");
+}
+
+void AutomatonIterationEpsilonTransition::Visit(void*, const automaton::VisiblyPushdownNPDA&) const
+{
+	throw exception::AlibException("Unsupported automaton type VisiblyPushdownNPDA");
+}
+
+void AutomatonIterationEpsilonTransition::Visit(void*, const automaton::OneTapeDTM&) const
+{
+	throw exception::AlibException("Unsupported automaton type OneTapeDTM");
+}
+
+const AutomatonIterationEpsilonTransition AutomatonIterationEpsilonTransition::AUTOMATON_ITERATION_EPSILON_TRANSITION;
+
+} /* namespace transform */
+
+} /* namespace automaton */
diff --git a/alib2algo/src/automaton/transform/AutomatonIterationEpsilonTransition.h b/alib2algo/src/automaton/transform/AutomatonIterationEpsilonTransition.h
new file mode 100644
index 0000000000000000000000000000000000000000..7329e8cfb50fdb8ce60fdf0c1033f4e18a89c53f
--- /dev/null
+++ b/alib2algo/src/automaton/transform/AutomatonIterationEpsilonTransition.h
@@ -0,0 +1,59 @@
+/*
+ * AutomatonIterationEpsilonTransition.h
+ *
+ *  Created on: 29. 11. 2014
+ *	  Author: Tomas Pecka
+ */
+
+#ifndef AUTOMATON_ITERATION_EPSILON_TRANSITION_H_
+#define AUTOMATON_ITERATION_EPSILON_TRANSITION_H_
+
+#include <automaton/Automaton.h>
+#include <automaton/FSM/EpsilonNFA.h>
+
+namespace automaton
+{
+
+namespace transform
+{
+
+/**
+ * Iterates language given by automaton
+ *  - For finite automaton A1, we create automaton L accepting L(A1)*
+ */
+class AutomatonIterationEpsilonTransition : public automaton::VisitableAutomatonBase::const_visitor_type
+{
+public:
+	static automaton::Automaton iteration(const automaton::Automaton& automaton);
+
+	template<class T>
+	static automaton::EpsilonNFA iteration(const T& automaton);
+
+private:
+	void Visit(void* data, const automaton::CompactNFA& automaton) const;
+	void Visit(void* data, const automaton::DFA& automaton) const;
+	void Visit(void* data, const automaton::EpsilonNFA& automaton) const;
+	void Visit(void* data, const automaton::ExtendedNFA& automaton) const;
+	void Visit(void* data, const automaton::MultiInitialStateNFA& automaton) const;
+	void Visit(void* data, const automaton::NFA& automaton) const;
+
+	void Visit(void* data, const automaton::DPDA& automaton) const;
+	void Visit(void* data, const automaton::NPDA& automaton) const;
+	void Visit(void* data, const automaton::InputDrivenNPDA& automaton) const;
+	void Visit(void* data, const automaton::RealTimeHeightDeterministicDPDA& automaton) const;
+	void Visit(void* data, const automaton::RealTimeHeightDeterministicNPDA& automaton) const;
+	void Visit(void* data, const automaton::SinglePopNPDA& automaton) const;
+	void Visit(void* data, const automaton::SinglePopDPDA& automaton) const;
+	void Visit(void* data, const automaton::VisiblyPushdownDPDA& automaton) const;
+	void Visit(void* data, const automaton::VisiblyPushdownNPDA& automaton) const;
+
+	void Visit(void* data, const automaton::OneTapeDTM& automaton) const;
+
+	static const AutomatonIterationEpsilonTransition AUTOMATON_ITERATION_EPSILON_TRANSITION;
+};
+
+} /* namespace transform */
+
+} /* namespace automaton */
+
+#endif /* AUTOMATON_ITERATION_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_ */
diff --git a/alib2algo/test-src/automaton/transform/AutomataConcatenationTest.cpp b/alib2algo/test-src/automaton/transform/AutomataConcatenationTest.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4acd4ed33f15b9ab84b94f63db15b691436d3200
--- /dev/null
+++ b/alib2algo/test-src/automaton/transform/AutomataConcatenationTest.cpp
@@ -0,0 +1,87 @@
+#include <list>
+#include "AutomataConcatenationTest.h"
+
+#include "automaton/transform/AutomataConcatenation.h"
+#include "automaton/transform/AutomataConcatenationEpsilonTransition.h"
+
+#include "automaton/simplify/MinimizeBrzozowski.h"
+#include "automaton/simplify/Normalize.h"
+#include "automaton/simplify/EpsilonRemover.h"
+#include "automaton/simplify/Trim.h"
+#include "automaton/simplify/Total.h"
+#include "automaton/determinize/Determinize.h"
+
+#include <factory/DataFactory.hpp>
+
+#define CPPUNIT_IMPLY(x, y) CPPUNIT_ASSERT(!(x) || (y))
+
+CPPUNIT_TEST_SUITE_REGISTRATION( AutomataConcatenationTest );
+
+void AutomataConcatenationTest::setUp() {
+}
+
+void AutomataConcatenationTest::tearDown() {
+}
+
+void AutomataConcatenationTest::testAutomataConcatenation() {
+	// based on Melichar, 2.79
+
+	automaton::State q1a("1"), q2a("2"), q0a("0"), q1b("1'"), q2b("2'"), q0b("0'");
+	automaton::State q0102("q0102");
+	alphabet::Symbol a(alphabet::symbolFrom('a')), b(alphabet::symbolFrom('b'));
+
+	automaton::DFA m1(q1a);
+	automaton::DFA m2(q1b);
+	automaton::NFA m3(q1a);
+
+	m1.setInputSymbols({a, b});
+	m1.setStates({q1a, q2a, q0a});
+	m1.addTransition(q1a, a, q2a);
+	m1.addTransition(q1a, b, q0a);
+	m1.addTransition(q2a, a, q2a);
+	m1.addTransition(q2a, b, q0a);
+	m1.addTransition(q0a, a, q0a);
+	m1.addTransition(q0a, b, q0a);
+	m1.addFinalState(q2a);
+
+	m2.setInputSymbols({a, b});
+	m2.setStates({q1b, q2b});
+	m2.addTransition(q1b, b, q2b);
+	m2.addTransition(q2b, b, q2b);
+	m2.addFinalState(q2b);
+
+	m3.setInputSymbols({a, b});
+	m3.setStates({q1a, q1b, q2a, q2b, q0a, q0b, q0102});
+	m3.addTransition(q1a, a, q2a);
+	m3.addTransition(q1a, a, q1b);
+	m3.addTransition(q1a, b, q0a);
+	m3.addTransition(q2a, a, q2a);
+	m3.addTransition(q2a, a, q1b);
+	m3.addTransition(q2a, b, q0a);
+	m3.addTransition(q0a, a, q0a);
+	m3.addTransition(q0a, b, q0a);
+	m3.addTransition(q1b, a, q0b);
+	m3.addTransition(q1b, b, q2b);
+	m3.addTransition(q2b, a, q0b);
+	m3.addTransition(q2b, b, q2b);
+	m3.addTransition(q0b, a, q0b);
+	m3.addTransition(q0b, b, q0b);
+	m3.setFinalStates({q2b});
+
+	auto u11 = automaton::transform::AutomataConcatenationEpsilonTransition::concatenation(automaton::Automaton(automaton::DFA(m1)), automaton::Automaton(automaton::NFA(m2)));
+	auto u12 = automaton::transform::AutomataConcatenationEpsilonTransition::concatenation(automaton::Automaton(automaton::DFA(m1)), automaton::Automaton(automaton::DFA(m2)));
+	auto u21 = automaton::transform::AutomataConcatenation::concatenation(automaton::Automaton(automaton::DFA(m1)), automaton::Automaton(automaton::NFA(m2)));
+	auto u22 = automaton::transform::AutomataConcatenation::concatenation(automaton::Automaton(automaton::DFA(m1)), automaton::Automaton(automaton::DFA(m2)));
+
+	automaton::Automaton umdfa  (automaton::simplify::Normalize::normalize(automaton::simplify::Trim::trim(automaton::simplify::MinimizeBrzozowski::minimize(automaton::simplify::EpsilonRemover::remove(m3)))));
+	automaton::Automaton umdfa11(automaton::simplify::Normalize::normalize(automaton::simplify::Trim::trim(automaton::simplify::MinimizeBrzozowski::minimize(automaton::simplify::EpsilonRemover::remove(u11)))));
+	automaton::Automaton umdfa12(automaton::simplify::Normalize::normalize(automaton::simplify::Trim::trim(automaton::simplify::MinimizeBrzozowski::minimize(automaton::simplify::EpsilonRemover::remove(u12)))));
+	automaton::Automaton umdfa21(automaton::simplify::Normalize::normalize(automaton::simplify::Trim::trim(automaton::simplify::MinimizeBrzozowski::minimize(automaton::simplify::EpsilonRemover::remove(u21)))));
+	automaton::Automaton umdfa22(automaton::simplify::Normalize::normalize(automaton::simplify::Trim::trim(automaton::simplify::MinimizeBrzozowski::minimize(automaton::simplify::EpsilonRemover::remove(u22)))));
+
+	CPPUNIT_ASSERT(umdfa11 == umdfa);
+	CPPUNIT_ASSERT(umdfa12 == umdfa);
+	CPPUNIT_ASSERT(umdfa21 == umdfa);
+	CPPUNIT_ASSERT(umdfa22 == umdfa);
+
+}
diff --git a/alib2algo/test-src/automaton/transform/AutomataConcatenationTest.h b/alib2algo/test-src/automaton/transform/AutomataConcatenationTest.h
new file mode 100644
index 0000000000000000000000000000000000000000..aa7dbecb205d1ff070f96c0498dce44fa3946343
--- /dev/null
+++ b/alib2algo/test-src/automaton/transform/AutomataConcatenationTest.h
@@ -0,0 +1,19 @@
+#ifndef AUTOMATA_CONCAT_TEST_H_
+#define AUTOMATA_CONCAT_TEST_H_
+
+#include <cppunit/extensions/HelperMacros.h>
+
+class AutomataConcatenationTest : public CppUnit::TestFixture
+{
+  CPPUNIT_TEST_SUITE( AutomataConcatenationTest );
+  CPPUNIT_TEST( testAutomataConcatenation );
+  CPPUNIT_TEST_SUITE_END();
+
+public:
+  void setUp();
+  void tearDown();
+
+  void testAutomataConcatenation();
+};
+
+#endif /* AUTOMATA_CONCAT_TEST_H_ */
diff --git a/alib2algo/test-src/automaton/transform/AutomataUnionTest.cpp b/alib2algo/test-src/automaton/transform/AutomataUnionTest.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..fe6d35161b8214812a9806bf673f8919f905581f
--- /dev/null
+++ b/alib2algo/test-src/automaton/transform/AutomataUnionTest.cpp
@@ -0,0 +1,76 @@
+#include <list>
+#include "AutomataUnionTest.h"
+
+#include "automaton/transform/AutomataUnionCartesianProduct.h"
+#include "automaton/transform/AutomataUnionEpsilonTransition.h"
+#include "automaton/simplify/MinimizeBrzozowski.h"
+#include "automaton/simplify/Normalize.h"
+#include "automaton/simplify/EpsilonRemover.h"
+#include "automaton/simplify/Trim.h"
+#include "automaton/simplify/Total.h"
+#include "automaton/determinize/Determinize.h"
+
+#include <factory/DataFactory.hpp>
+
+#define CPPUNIT_IMPLY(x, y) CPPUNIT_ASSERT(!(x) || (y))
+
+CPPUNIT_TEST_SUITE_REGISTRATION( AutomataUnionTest );
+
+void AutomataUnionTest::setUp() {
+}
+
+void AutomataUnionTest::tearDown() {
+}
+
+void AutomataUnionTest::testAutomataUnion() {
+	// based on Melichar, 2.72
+
+	automaton::State q1a("1"), q2a("2"), q0a("0"), q1b("1'"), q2b("2'"), q0b("0'");
+	automaton::State q11("11"), q20("20"), q00("00"), q02("02");
+	alphabet::Symbol a(alphabet::symbolFrom('a')), b(alphabet::symbolFrom('b'));
+
+	automaton::DFA m1(q1a);
+	automaton::DFA m2(q1b);
+	automaton::DFA m3(q11);
+
+	m1.setInputSymbols({a, b});
+	m1.setStates({q1a, q2a, q0a});
+	m1.addTransition(q1a, a, q2a);
+	m1.addTransition(q1a, b, q0a);
+	m1.addTransition(q2a, a, q2a);
+	m1.addTransition(q2a, b, q0a);
+	m1.addTransition(q0a, a, q0a);
+	m1.addTransition(q0a, b, q0a);
+	m1.addFinalState(q2a);
+
+	m2.setInputSymbols({a, b});
+	m2.setStates({q1b, q2b});
+	m2.addTransition(q1b, b, q2b);
+	m2.addTransition(q2b, b, q2b);
+	m2.addFinalState(q2b);
+
+	m3.setInputSymbols({a, b});
+	m3.setStates({q11, q20, q00, q02});
+	m3.addTransition(q11, a, q20);
+	m3.addTransition(q11, b, q02);
+	m3.addTransition(q20, a, q20);
+	m3.addTransition(q20, b, q00);
+	m3.addTransition(q00, a, q00);
+	m3.addTransition(q00, b, q00);
+	m3.addTransition(q02, a, q00);
+	m3.addTransition(q02, b, q02);
+	m3.setFinalStates({q20, q02});
+
+	auto u1 = automaton::transform::AutomataUnionEpsilonTransition::unification(automaton::Automaton(m1), automaton::Automaton(m2));
+	CPPUNIT_ASSERT_THROW(automaton::transform::AutomataUnionCartesianProduct::unification(automaton::Automaton(m1), automaton::Automaton(m2)), exception::AlibException);
+	CPPUNIT_ASSERT_THROW(automaton::transform::AutomataUnionCartesianProduct::unification(automaton::Automaton(automaton::NFA(m1)), automaton::Automaton(m2)), exception::AlibException);
+	auto u2 = automaton::transform::AutomataUnionEpsilonTransition::unification(automaton::Automaton(automaton::simplify::Total::total(m1)), automaton::Automaton(automaton::simplify::Total::total(m2)));
+
+	automaton::Automaton umdfa(automaton::simplify::Normalize::normalize(automaton::simplify::Trim::trim(automaton::simplify::MinimizeBrzozowski::minimize(automaton::simplify::EpsilonRemover::remove(m3)))));
+	automaton::Automaton umdfa1(automaton::simplify::Normalize::normalize(automaton::simplify::Trim::trim(automaton::simplify::MinimizeBrzozowski::minimize(automaton::simplify::EpsilonRemover::remove(u1)))));
+	automaton::Automaton umdfa2(automaton::simplify::Normalize::normalize(automaton::simplify::Trim::trim(automaton::simplify::MinimizeBrzozowski::minimize(automaton::simplify::EpsilonRemover::remove(u2)))));
+
+	CPPUNIT_ASSERT(umdfa1 == umdfa);
+	CPPUNIT_ASSERT(umdfa2 == umdfa);
+
+}
diff --git a/alib2algo/test-src/automaton/transform/AutomataUnionTest.h b/alib2algo/test-src/automaton/transform/AutomataUnionTest.h
new file mode 100644
index 0000000000000000000000000000000000000000..d45ef404d8ee8f8996ef4079d6d8e1de1dbb9890
--- /dev/null
+++ b/alib2algo/test-src/automaton/transform/AutomataUnionTest.h
@@ -0,0 +1,19 @@
+#ifndef AUTOMATA_UNION_TEST_H_
+#define AUTOMATA_UNION_TEST_H_
+
+#include <cppunit/extensions/HelperMacros.h>
+
+class AutomataUnionTest : public CppUnit::TestFixture
+{
+  CPPUNIT_TEST_SUITE( AutomataUnionTest );
+  CPPUNIT_TEST( testAutomataUnion );
+  CPPUNIT_TEST_SUITE_END();
+
+public:
+  void setUp();
+  void tearDown();
+
+  void testAutomataUnion();
+};
+
+#endif /* AUTOMATA_UNION_TEST_H_ */
diff --git a/alib2algo/test-src/automaton/transform/AutomatonIterationTest.cpp b/alib2algo/test-src/automaton/transform/AutomatonIterationTest.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ae36365ddaff7a84b2678e7e5579e4abae3194b2
--- /dev/null
+++ b/alib2algo/test-src/automaton/transform/AutomatonIterationTest.cpp
@@ -0,0 +1,58 @@
+#include <list>
+#include "AutomatonIterationTest.h"
+
+#include "automaton/transform/AutomatonIteration.h"
+#include "automaton/transform/AutomatonIterationEpsilonTransition.h"
+
+#include "automaton/simplify/MinimizeBrzozowski.h"
+#include "automaton/simplify/Normalize.h"
+#include "automaton/simplify/EpsilonRemover.h"
+#include "automaton/simplify/Trim.h"
+#include "automaton/simplify/Total.h"
+#include "automaton/determinize/Determinize.h"
+
+#include <factory/DataFactory.hpp>
+
+#define CPPUNIT_IMPLY(x, y) CPPUNIT_ASSERT(!(x) || (y))
+
+CPPUNIT_TEST_SUITE_REGISTRATION( AutomatonIterationTest );
+
+void AutomatonIterationTest::setUp() {
+}
+
+void AutomatonIterationTest::tearDown() {
+}
+
+void AutomatonIterationTest::testAutomatonIteration() {
+
+	// Melichar 2.83
+	automaton::State q1(1), q2(2), q3(3);
+	alphabet::Symbol a(alphabet::symbolFrom('a')), b(alphabet::symbolFrom('b'));
+
+	automaton::DFA m1(q1);
+	m1.setStates({q1, q2, q3});
+	m1.addFinalState(q3);
+	m1.setInputSymbols({a, b});
+	m1.addTransition(q1, a, q2);
+	m1.addTransition(q2, b, q2);
+	m1.addTransition(q2, a, q3);
+
+	automaton::NFA res(q1);
+	res.setStates({q1, q2, q3});
+	res.setInputSymbols({a, b});
+	res.setFinalStates({q1, q3});
+	res.addTransition(q1, a, q2);
+	res.addTransition(q2, b, q2);
+	res.addTransition(q2, a, q3);
+	res.addTransition(q2, a, q1);
+
+	automaton::Automaton i2 = automaton::transform::AutomatonIterationEpsilonTransition::iteration(automaton::Automaton(m1));
+	automaton::Automaton i1 = automaton::transform::AutomatonIteration::iteration(automaton::Automaton(m1));
+
+	automaton::Automaton mdfa1 (automaton::simplify::Normalize::normalize(automaton::simplify::Trim::trim(automaton::simplify::MinimizeBrzozowski::minimize(automaton::simplify::EpsilonRemover::remove(i1)))));
+	automaton::Automaton mdfa2 (automaton::simplify::Normalize::normalize(automaton::simplify::Trim::trim(automaton::simplify::MinimizeBrzozowski::minimize(automaton::simplify::EpsilonRemover::remove(i2)))));
+	automaton::Automaton mdfa3 (automaton::simplify::Normalize::normalize(automaton::simplify::Trim::trim(automaton::simplify::MinimizeBrzozowski::minimize(automaton::simplify::EpsilonRemover::remove(res)))));
+
+	CPPUNIT_ASSERT(mdfa1 == mdfa2);
+	CPPUNIT_ASSERT(mdfa1 == mdfa3);
+}
diff --git a/alib2algo/test-src/automaton/transform/AutomatonIterationTest.h b/alib2algo/test-src/automaton/transform/AutomatonIterationTest.h
new file mode 100644
index 0000000000000000000000000000000000000000..28119050acdb90a63d8b8ebd524739c0b58ba364
--- /dev/null
+++ b/alib2algo/test-src/automaton/transform/AutomatonIterationTest.h
@@ -0,0 +1,19 @@
+#ifndef AUTOMATA_ITER_TEST_H_
+#define AUTOMATA_ITER_TEST_H_
+
+#include <cppunit/extensions/HelperMacros.h>
+
+class AutomatonIterationTest : public CppUnit::TestFixture
+{
+  CPPUNIT_TEST_SUITE( AutomatonIterationTest );
+  CPPUNIT_TEST( testAutomatonIteration );
+  CPPUNIT_TEST_SUITE_END();
+
+public:
+  void setUp();
+  void tearDown();
+
+  void testAutomatonIteration();
+};
+
+#endif /* AUTOMATA_ITER_TEST_H_ */
diff --git a/makefile b/makefile
index 5a2044bc2bf0e4b9b62c951e440219cb2a395e94..a7efa5c1a5b973de046b89dcd0b9580dd7a6195d 100644
--- a/makefile
+++ b/makefile
@@ -12,6 +12,7 @@ SUBDIRS_BINS = acat2 \
 		adiff2 \
 		aepsilon2 \
 		aintegral2 \
+		alangop2 \
 		aminimize2 \
 		anormalize2 \
 		arand2 \