From 1b6f3bd53b3ca06424d8531840ce9e30e50db366 Mon Sep 17 00:00:00 2001
From: Tomas Pecka <tomas.pecka@fit.cvut.cz>
Date: Sat, 9 Nov 2019 12:09:07 +0100
Subject: [PATCH] LatexConverter for finite automata and RRG/CFG (Closes #172).

---
 alib2aux/src/convert/LatexConverter.cpp |  29 +++
 alib2aux/src/convert/LatexConverter.h   | 297 ++++++++++++++++++++++++
 alib2aux/src/convert/LatexTable.cpp     |  15 --
 alib2aux/src/convert/LatexTable.h       |  88 -------
 4 files changed, 326 insertions(+), 103 deletions(-)
 create mode 100644 alib2aux/src/convert/LatexConverter.cpp
 create mode 100644 alib2aux/src/convert/LatexConverter.h
 delete mode 100644 alib2aux/src/convert/LatexTable.cpp
 delete mode 100644 alib2aux/src/convert/LatexTable.h

diff --git a/alib2aux/src/convert/LatexConverter.cpp b/alib2aux/src/convert/LatexConverter.cpp
new file mode 100644
index 0000000000..4c1c5ff3b3
--- /dev/null
+++ b/alib2aux/src/convert/LatexConverter.cpp
@@ -0,0 +1,29 @@
+/*
+ * LatexConverter.cpp
+ *
+ *  Created on: 6. 11. 2017
+ *      Author: Jan Travnicek, Tomas Pecka
+ */
+
+#include "LatexConverter.h"
+#include <registration/AlgoRegistration.hpp>
+
+namespace {
+
+auto DFA = registration::AbstractRegister < convert::LatexConverter, std::string, const automaton::DFA < > & > ( convert::LatexConverter::convertFSM, "automaton" ).setDocumentation ( "Converts a DFA table representation to Latex." );
+auto NFA = registration::AbstractRegister < convert::LatexConverter, std::string, const automaton::NFA < > & > ( convert::LatexConverter::convertFSM ).setDocumentation ( "Converts a NFA table representation to Latex." );
+auto ENFA = registration::AbstractRegister < convert::LatexConverter, std::string, const automaton::EpsilonNFA < > & > ( convert::LatexConverter::convertFSM ).setDocumentation ( "Converts a EpsilonNFA table representation to Latex." );
+auto MISNFA = registration::AbstractRegister < convert::LatexConverter, std::string, const automaton::MultiInitialStateNFA < > & > ( convert::LatexConverter::convertFSM ).setDocumentation ( "Converts a MultiInitialStateNFA table representation to Latex." );
+auto MISENFA = registration::AbstractRegister < convert::LatexConverter, std::string, const automaton::MultiInitialStateEpsilonNFA < > & > ( convert::LatexConverter::convertFSM ).setDocumentation ( "Converts a MultiInitialStateEpsilonNFA table representation to Latex." );
+
+auto DFAw = registration::AbstractRegister < convert::LatexConverter, std::string, const automaton::DFA < > &, bool > ( convert::LatexConverter::convertFSM, "automaton", "wide" ).setDocumentation ( "Converts a DFA table representation to Latex. If 'wide' is true, create an empty column and make it as wide as the page. This is useful for instance for test generation of minimization algorithm." );
+auto NFAw = registration::AbstractRegister < convert::LatexConverter, std::string, const automaton::NFA < > &, bool > ( convert::LatexConverter::convertFSM, "automaton", "wide" ).setDocumentation ( "Converts a DFA table representation to Latex. If 'wide' is true, create an empty column and make it as wide as the page. This is useful for instance for test generation of minimization algorithm." );
+auto ENFAw = registration::AbstractRegister < convert::LatexConverter, std::string, const automaton::EpsilonNFA < > &, bool > ( convert::LatexConverter::convertFSM, "automaton", "wide" ).setDocumentation ( "Converts a DFA table representation to Latex. If 'wide' is true, create an empty column and make it as wide as the page. This is useful for instance for test generation of minimization algorithm." );
+auto MISNFAw = registration::AbstractRegister < convert::LatexConverter, std::string, const automaton::MultiInitialStateNFA < > &, bool > ( convert::LatexConverter::convertFSM, "automaton", "wide" ).setDocumentation ( "Converts a DFA table representation to Latex. If 'wide' is true, create an empty column and make it as wide as the page. This is useful for instance for test generation of minimization algorithm." );
+auto MISENFAw = registration::AbstractRegister < convert::LatexConverter, std::string, const automaton::MultiInitialStateEpsilonNFA < > &, bool > ( convert::LatexConverter::convertFSM, "automaton", "wide" ).setDocumentation ( "Converts a DFA table representation to Latex. If 'wide' is true, create an empty column and make it as wide as the page. This is useful for instance for test generation of minimization algorithm." );
+
+auto CFG = registration::AbstractRegister < convert::LatexConverter, std::string, const grammar::CFG < > & > ( convert::LatexConverter::convertGrammar, "grammar" ).setDocumentation ( "Converts CFG grammar to Latex representation." );
+auto RRG = registration::AbstractRegister < convert::LatexConverter, std::string, const grammar::RightRG < > & > ( convert::LatexConverter::convertGrammar, "grammar" ).setDocumentation ( "Converts (right) regular grammar to Latex representation." );
+
+
+} /* namespace */
diff --git a/alib2aux/src/convert/LatexConverter.h b/alib2aux/src/convert/LatexConverter.h
new file mode 100644
index 0000000000..5ba8dd156c
--- /dev/null
+++ b/alib2aux/src/convert/LatexConverter.h
@@ -0,0 +1,297 @@
+/*
+ * LatexConverter.h
+ *
+ *  Created on: 6. 11. 2017
+ *      Author: Jan Travnicek, Tomas Pecka
+ */
+
+#ifndef LATEX_TABLE_H_
+#define LATEX_TABLE_H_
+
+#include <ostream>
+#include <string/String.h>
+
+#include <automaton/FSM/DFA.h>
+#include <automaton/FSM/NFA.h>
+#include <automaton/FSM/EpsilonNFA.h>
+#include <automaton/FSM/MultiInitialStateNFA.h>
+#include <automaton/FSM/MultiInitialStateEpsilonNFA.h>
+
+#include <grammar/ContextFree/CFG.h>
+#include <grammar/Regular/RightRG.h>
+
+#include "common/converterCommon.hpp"
+
+#include <factory/StringDataFactory.hpp>
+
+namespace convert {
+
+class LatexConverter {
+public:
+	template < class FiniteAutomatonType >
+	static void convertFSM ( std::ostream & out, const FiniteAutomatonType & automaton, bool wideTable = false );
+
+	template < class FiniteAutomatonType >
+	static std::string convertFSM ( const FiniteAutomatonType & automaton ) {
+		return convertFSM ( automaton, false );
+	}
+
+	template < class FiniteAutomatonType >
+	static std::string convertFSM ( const FiniteAutomatonType & automaton, bool wideTable ) {
+		std::stringstream ss;
+		convertFSM ( ss, automaton, wideTable );
+		return ss.str ( );
+	}
+
+	/* --------------------------------------------------------------------- */
+
+	template < class TerminalSymbolType, class NonterminalSymbolType >
+	static void rules ( std::ostream & out, const grammar::CFG < TerminalSymbolType, NonterminalSymbolType > & grammar );
+
+	template < class TerminalSymbolType, class NonterminalSymbolType >
+	static void rules ( std::ostream & out, const grammar::RightRG < TerminalSymbolType, NonterminalSymbolType > & grammar );
+
+	template < class GrammarType >
+	static void convertGrammar ( std::ostream & out, const GrammarType & grammar );
+
+	template < class GrammarType >
+	static std::string convertGrammar ( const GrammarType & grammar ) {
+		std::stringstream ss;
+		convertGrammar ( ss, grammar );
+		return ss.str ( );
+	}
+
+private:
+	template < class SymbolType, class StateType >
+	static constexpr const char* automatonType ( const automaton::DFA < SymbolType, StateType > & ) {
+		return "DFA";
+	}
+
+	template < class SymbolType, class StateType >
+	static constexpr const char* automatonType ( const automaton::NFA < SymbolType, StateType > & ) {
+		return "NFA";
+	}
+
+	template < class SymbolType, class StateType >
+	static constexpr const char* automatonType ( const automaton::EpsilonNFA < SymbolType, StateType > & ) {
+		return "$\\varepsilon$-NFA";
+	}
+
+	template < class SymbolType, class StateType >
+	static constexpr const char* automatonType ( const automaton::MultiInitialStateNFA < SymbolType, StateType > & ) {
+		return "NFA";
+	}
+
+	template < class SymbolType, class StateType >
+	static constexpr const char* automatonType ( const automaton::MultiInitialStateEpsilonNFA < SymbolType, StateType > & ) {
+		return "$\\varepsilon$-NFA";
+	}
+
+	/* --------------------------------------------------------------------- */
+
+	template < class FiniteAutomatonType, class StateType >
+	static inline ext::require < automaton::isMultiInitialStateNFA < FiniteAutomatonType > || automaton::isMultiInitialStateEpsilonNFA < FiniteAutomatonType >, bool > isInitialState ( const FiniteAutomatonType & automaton, const StateType & state ) {
+		return automaton.getInitialStates ( ).count ( state );
+	}
+
+	template < class FiniteAutomatonType, class StateType >
+	static inline ext::require < automaton::isDFA < FiniteAutomatonType > || automaton::isNFA < FiniteAutomatonType > || automaton::isEpsilonNFA < FiniteAutomatonType >, bool > isInitialState ( const FiniteAutomatonType & automaton, const StateType & state ) {
+		return automaton.getInitialState ( ) == state;
+	}
+
+	/* --------------------------------------------------------------------- */
+
+	template < class FiniteAutomatonType, class StateType >
+	static ext::require < automaton::isDFA < FiniteAutomatonType >, void > transitionsRow ( std::ostream & out, const FiniteAutomatonType & automaton, const StateType & state ) {
+		for ( const auto & symbol : automaton.getInputAlphabet ( ) ) {
+			out << " & ";
+
+			auto transition = automaton.getTransitions ( ).find ( ext::make_pair ( state, symbol ) );
+			if ( transition == automaton.getTransitions ( ).end ( ) )
+				out << "-";
+			else
+				out << replace ( factory::StringDataFactory::toString ( transition -> second ), "\"", "\\\"" );
+		}
+	}
+
+	template < class FiniteAutomatonType, class StateType >
+	static ext::require < automaton::isNFA < FiniteAutomatonType > || automaton::isMultiInitialStateNFA < FiniteAutomatonType >, void > transitionsRow ( std::ostream & out, const FiniteAutomatonType & automaton, const StateType & state ) {
+		for ( const auto & symbol : automaton.getInputAlphabet ( ) ) {
+			out << " & ";
+
+			auto transitions = automaton.getTransitions ( ).equal_range ( ext::make_pair ( state, symbol ) );
+
+			for ( auto it = transitions.begin ( ); it != transitions.end ( ); ++ it ) {
+				if ( it != transitions.begin ( ) )
+					out << ",";
+				out << replace ( factory::StringDataFactory::toString ( it->second ), "\"", "\\\"" );
+			}
+
+			if ( transitions.empty ( ) )
+				out << "-";
+		}
+	}
+
+	template < class FiniteAutomatonType, class StateType >
+	static ext::require < automaton::isEpsilonNFA < FiniteAutomatonType > || automaton::isMultiInitialStateEpsilonNFA < FiniteAutomatonType >, void > transitionsRow ( std::ostream & out, const FiniteAutomatonType & automaton, const StateType & state ) {
+		const auto symTransitions = automaton.getSymbolTransitions ( );
+		const auto epsTransitions = automaton.getEpsilonTransitions ( );
+
+		/* columns for symbols */
+		for ( const auto &  symbol : automaton.getInputAlphabet ( ) ) {
+			out << " & ";
+
+			auto tr = symTransitions.equal_range ( ext::make_pair ( state, symbol ) );
+
+			if ( tr.empty ( ) )
+				out << "-";
+
+			for ( auto it = tr.begin ( ); it != tr.end ( ); ++ it ) {
+				if ( it != tr.begin ( ) )
+					out << ",";
+				out << replace ( factory::StringDataFactory::toString ( it -> second ), "\"", "\\\"" );
+			}
+		}
+
+		/* column for epsilon */
+		out << " & ";
+		auto tr = epsTransitions.equal_range ( state );
+
+		if ( tr.empty ( ) )
+			out << "-";
+
+		for ( auto it = tr.begin ( ); it != tr.end ( ); ++ it ) {
+			if ( it != tr.begin ( ) )
+				out << ",";
+			out << replace ( factory::StringDataFactory::toString ( it->second ), "\"", "\\\"" );
+		}
+	}
+
+};
+
+template < class FiniteAutomatonType >
+void LatexConverter::convertFSM ( std::ostream & out, const FiniteAutomatonType & automaton, bool wideTable ) {
+	constexpr bool isEpsilonType = automaton::isMultiInitialStateEpsilonNFA < FiniteAutomatonType > || automaton::isEpsilonNFA < FiniteAutomatonType >;
+
+	if( wideTable )
+		out << "\\begin{tabular*}{\\textwidth}{|rl||";
+	else
+		out << "\\begin{tabular}{|rl||";
+
+	for ( size_t i = 0; i < automaton.getInputAlphabet ( ).size ( ) + isEpsilonType ; ++ i )
+		out << "c|";
+	out << "}" << std::endl;
+
+	out << "\\hline" << std::endl;
+	out << "\\multicolumn{2}{|c||}{" << automatonType ( automaton ) << "}";
+
+	for ( const auto & symbol : automaton.getInputAlphabet ( ) )
+		out << " & " << replace ( factory::StringDataFactory::toString ( symbol ), "\"", "\\\"" );
+
+	if constexpr ( isEpsilonType )
+		out << " & $\\varepsilon$";
+
+	// out << "\\hspace*{14cm}\\\\" << std::endl;
+	out << "\\\\\\hline" << std::endl;
+
+	for ( const auto & state : automaton.getStates ( ) ) {
+		if ( automaton.getFinalStates ( ).count ( state ) && isInitialState ( automaton, state ) )
+			out << "$\\leftrightarrow$ & ";
+		else if ( automaton.getFinalStates ( ).count ( state ) )
+			out << "$\\leftarrow$      & ";
+		else if ( isInitialState ( automaton, state ) )
+			out << "$\\rightarrow$     & ";
+		else
+			out << "                  & ";
+
+		out << replace ( factory::StringDataFactory::toString ( state ), "\"", "\\\"" );
+		transitionsRow ( out, automaton, state );
+		out << " \\\\\\hline" << std::endl;
+	}
+
+	if ( wideTable )
+		out << "\\end{tabular*}" << std::endl;
+	else
+		out << "\\end{tabular}" << std::endl;
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+template < class TerminalSymbolType, class NonterminalSymbolType >
+void LatexConverter::rules ( std::ostream & out, const grammar::CFG < TerminalSymbolType, NonterminalSymbolType > & grammar ) {
+	for ( const auto & kv : grammar.getRules ( ) ) {
+		const auto & lhs = kv.first;
+
+		if ( kv.second.size ( ) > 0 )
+			out << replace ( factory::StringDataFactory::toString ( lhs ), "\"", "\\\"" ) << " & \\rightarrow & ";
+
+		for ( auto itRhs = kv.second.begin ( ); itRhs != kv.second.end ( ); ++ itRhs ) {
+			if ( itRhs != kv.second.begin ( ) )
+				out << " \\mid ";
+
+			for ( const auto & symb : *itRhs ) {
+				if ( symb.template is < TerminalSymbolType > ( ) )
+					out << replace ( factory::StringDataFactory::toString ( symb.template get < TerminalSymbolType > ( ) ), "\"", "\\\"" ) << " ";
+				else if ( symb.template is < NonterminalSymbolType > ( ) )
+					out << replace ( factory::StringDataFactory::toString ( symb.template get < NonterminalSymbolType > ( ) ), "\"", "\\\"" ) << " ";
+			}
+			if ( itRhs -> empty ( ) )
+				out << "\\varepsilon";
+		}
+		out << "\\\\" << std::endl;
+	}
+}
+
+template < class TerminalSymbolType, class NonterminalSymbolType >
+void LatexConverter::rules ( std::ostream & out, const grammar::RightRG < TerminalSymbolType, NonterminalSymbolType > & grammar ) {
+	for ( const auto & kv : grammar.getRules ( ) ) {
+		const NonterminalSymbolType & lhs = kv.first;
+
+		if ( kv.second.size ( ) > 0 )
+			out << replace ( factory::StringDataFactory::toString ( lhs ), "\"", "\\\"" ) << " & \\rightarrow & ";
+
+		for ( auto itRhs = kv.second.begin ( ); itRhs != kv.second.end ( ); ++ itRhs ) {
+			if ( itRhs != kv.second.begin ( ) )
+				out << " \\mid ";
+
+			if ( itRhs -> template is < TerminalSymbolType > ( ) )
+				out << replace ( factory::StringDataFactory::toString ( itRhs -> template get < TerminalSymbolType > ( ) ), "\"", "\\\"" ) << " ";
+			else {
+				const ext::pair < TerminalSymbolType, NonterminalSymbolType > & rhs = itRhs -> template get < ext::pair < TerminalSymbolType, NonterminalSymbolType > > ( );
+				out << replace ( factory::StringDataFactory::toString ( rhs.first ), "\"", "\\\"" ) << " ";
+				out << replace ( factory::StringDataFactory::toString ( rhs.second ), "\"", "\\\"" ) << " ";
+			}
+		}
+		if ( lhs == grammar.getInitialSymbol ( ) && grammar.getGeneratesEpsilon ( ) )
+			out << " \\mid \\varepsilon";
+		out << "\\\\" << std::endl;
+	}
+}
+
+template < class GrammarType >
+void LatexConverter::convertGrammar ( std::ostream & out, const GrammarType & grammar ) {
+	out << "";
+	out << "$$G = (\\{";
+	for ( auto it = grammar.getNonterminalAlphabet ( ).begin ( ); it != grammar.getNonterminalAlphabet ( ).end ( ); ++ it ) {
+		if ( it != grammar.getNonterminalAlphabet ( ).begin ( ) )
+			out << ", ";
+		out << replace ( factory::StringDataFactory::toString ( *it ), "\"", "\\\"" );
+	}
+	out << "\\}, \\{";
+	for ( auto it = grammar.getTerminalAlphabet ( ).begin ( ); it != grammar.getTerminalAlphabet ( ).end ( ); ++ it ) {
+		if ( it != grammar.getTerminalAlphabet ( ).begin ( ) )
+			out << ", ";
+		out << replace ( factory::StringDataFactory::toString ( *it ), "\"", "\\\"" );
+	}
+	out << "\\}, P, ";
+	out << replace ( factory::StringDataFactory::toString ( grammar.getInitialSymbol ( ) ), "\"", "\\\"");
+	out << ")$$" << std::endl << std::endl;
+
+	out << "\\begin{eqnarray*}" << std::endl;
+	rules ( out, grammar );
+	out << "\\end{eqnarray*}" << std::endl;
+}
+
+} /* namespace convert */
+
+#endif /* LATEX_TABLE_H_ */
diff --git a/alib2aux/src/convert/LatexTable.cpp b/alib2aux/src/convert/LatexTable.cpp
deleted file mode 100644
index b8d39c1642..0000000000
--- a/alib2aux/src/convert/LatexTable.cpp
+++ /dev/null
@@ -1,15 +0,0 @@
-/*
- * LatexTable.cpp
- *
- *  Created on: 6. 11. 2017
- *      Author: Jan Travnicek
- */
-
-#include "LatexTable.h"
-#include <registration/AlgoRegistration.hpp>
-
-namespace {
-
-auto DFA = registration::AbstractRegister < convert::LatexTable, std::string, const automaton::DFA < > & > ( convert::LatexTable::convert );
-
-} /* namespace */
diff --git a/alib2aux/src/convert/LatexTable.h b/alib2aux/src/convert/LatexTable.h
deleted file mode 100644
index 24ca07cd86..0000000000
--- a/alib2aux/src/convert/LatexTable.h
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * LatexTable.h
- *
- *  Created on: 6. 11. 2017
- *      Author: Jan Travnicek
- */
-
-#ifndef LATEX_TABLE_H_
-#define LATEX_TABLE_H_
-
-#include <ostream>
-#include <alib/set>
-#include <alib/map>
-#include <alib/list>
-#include <alib/utility>
-#include <alib/vector>
-
-#include <exception/CommonException.h>
-#include <string/String.h>
-
-#include <automaton/FSM/DFA.h>
-
-#include "common/converterCommon.hpp"
-
-#include <factory/StringDataFactory.hpp>
-#include <string/string/LinearString.h>
-#include <regexp/string/UnboundedRegExp.h>
-
-namespace convert {
-
-class LatexTable {
-public:
-	template < class SymbolType, class StateType >
-	static void convert(std::ostream& out, const automaton::DFA < SymbolType, StateType > & a);
-
-	template < class T >
-	static std::string convert ( const T & automaton ) {
-		std::stringstream ss;
-		convert ( ss, automaton );
-		return ss.str ( );
-	}
-};
-
-template < class SymbolType, class StateType >
-void LatexTable::convert(std::ostream& out, const automaton::DFA < SymbolType, StateType > & a) {
-	size_t symbols = a.getInputAlphabet ( ).size ( );
-	out << "\\begin{tabular}{|lr||";
-	for ( size_t i = 0; i < symbols; ++ i )
-		out << "c|";
-	out << "l}" << std::endl;
-	out << "\\hline" << std::endl;
-	out << "                  &   & ";
-	for ( const SymbolType & symbol : a.getInputAlphabet ( ) )
-		out << replace ( factory::StringDataFactory::toString ( symbol ), "\"", "\\\"" ) << " & ";
-	out << "\\hspace*{14cm}\\\\" << std::endl;
-	out << "\\hline" << std::endl;
-
-	for ( const StateType & source : a.getStates ( ) ) {
-		out << "\\hline" << std::endl;
-		if ( a.getFinalStates ( ).count ( source ) && a.getInitialState ( ) == source )
-			out << "$\\leftrightarrow$ & ";
-		else if ( a.getFinalStates ( ).count ( source ) )
-			out << "$\\leftarrow$      & ";
-		else if ( a.getInitialState ( ) == source )
-			out << "$\\rightarrow$     & ";
-		else
-			out << "                  & ";
-
-		out << replace ( factory::StringDataFactory::toString ( source ), "\"", "\\\"" ) << " & ";
-
-		for ( const SymbolType & symbol : a.getInputAlphabet ( ) ) {
-			auto transition = a.getTransitions ( ).find ( ext::make_pair ( source, symbol ) );
-			if ( transition == a.getTransitions ( ).end ( ) )
-				out << "- & ";
-			else
-				out << replace ( factory::StringDataFactory::toString ( transition->second ), "\"", "\\\"" ) << " & ";
-		}
-
-		out << "\\\\" << std::endl;
-	}
-
-	out << "\\hline" << std::endl;
-	out << "\\end{tabular}" << std::endl;
-}
-
-} /* namespace convert */
-
-#endif /* LATEX_TABLE_H_ */
-- 
GitLab