/*
 * GrammarConcatenation.h
 *
 * This file is part of Algorithms library toolkit.
 * Copyright (C) 2017 Jan Travnicek (jan.travnicek@fit.cvut.cz)

 * Algorithms library toolkit is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.

 * Algorithms library toolkit is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with Algorithms library toolkit.  If not, see <http://www.gnu.org/licenses/>.
 *
 *  Created on: 05. 11. 2019
 *	  Author: Tomas Pecka
 */

#ifndef GRAMMAR_CONCATENATION_H_
#define GRAMMAR_CONCATENATION_H_

#include "GrammarTransformCommon.h"

#include <grammar/ContextFree/CFG.h>
#include <common/createUnique.hpp>

#include <label/InitialStateLabel.h>

namespace grammar::transform {

/**
 * Concatenation of two grammars.
 * For two regular grammars G1 and G2, we create a regular grammar such that L(G) = L(G1).L(G2).
 * For CFG and CFG/RG we create a context free grammar such that L(G) = L(G1).(G2).
 * Source:
 */
class GrammarConcatenation {
public:
	/**
	 * Concatenates two grammars.
	 * @tparam TerminalSymbolType Type for terminal symbols.
	 * @tparam NonterminalSymbolType Type for nonterminal symbols.
	 * @param first First grammar (G1)
	 * @param second Second grammar (G2)
	 * @return CFG grammar for concatenation of two CFG or RightRG for concatenation of two RightRG.
	 */
	template < class TerminalSymbolType, class NonterminalSymbolType >
	static grammar::CFG < TerminalSymbolType, ext::pair < NonterminalSymbolType, unsigned > > concatenation ( const grammar::CFG < TerminalSymbolType, NonterminalSymbolType > & first, const grammar::CFG < TerminalSymbolType, NonterminalSymbolType > & second );
};

template < class TerminalSymbolType, class NonterminalSymbolType >
grammar::CFG < TerminalSymbolType, ext::pair < NonterminalSymbolType, unsigned > > GrammarConcatenation::concatenation ( const grammar::CFG < TerminalSymbolType, NonterminalSymbolType > & first, const grammar::CFG < TerminalSymbolType, NonterminalSymbolType > & second ) {
	ext::pair < NonterminalSymbolType, unsigned > S = ext::make_pair ( common::createUnique ( label::InitialStateLabel::instance < NonterminalSymbolType > ( ), first.getNonterminalAlphabet ( ), second.getNonterminalAlphabet ( ) ), 0 );
	grammar::CFG < TerminalSymbolType, ext::pair < NonterminalSymbolType, unsigned > > res ( S );

	res.setTerminalAlphabet ( first.getTerminalAlphabet ( ) + second.getTerminalAlphabet ( ) );

	copyNonterminalsRename ( res, first, 1 );
	copyNonterminalsRename ( res, first, 2 );

	copyRulesRenameNonterminals ( res, first, 1 );
	copyRulesRenameNonterminals ( res, second, 2 );

	res.addRule ( S, { ext::make_pair ( first.getInitialSymbol ( ), 1 ), ext::make_pair ( second.getInitialSymbol ( ), 2 ) } );

	return res;
}

} /* namespace grammar::transform */

#endif /* GRAMMAR_CONCATENATION_H_ */
