/*
 * RandomTreeFactory.cpp
 *
 *  Created on: 18. 3. 2015
 *	  Author: Stepan Plachy
 */

#include "RandomTreeFactory.h"

#include <alib/vector>
#include <alib/map>
#include <alib/set>
#include <alib/algorithm>
#include <cstdlib>
#include <ctime>
#include <cmath>

#include <common/ranked_symbol.hpp>

#include <alib/iostream>
#include <alib/random>

#include <alphabet/WildcardSymbol.h>
#include <alphabet/NonlinearVariableSymbol.h>
#include <exception/CommonException.h>

#include <registration/AlgoRegistration.hpp>

#include <global/GlobalData.h>

namespace tree {

namespace generate {

struct Node {
	char symbol;
	int depth;
	Node * right;
	Node * child = nullptr;
	int rank = 0;

	Node ( ) : depth ( 0 ) { }
	Node ( Node * parent ) : depth ( parent->depth + 1 ) {
		if ( parent->child == nullptr ) {
			parent->child = this;
			right = this;
		} else {
			right = parent->child->right;
			parent->child->right = this;
		}

		parent->rank++;
	}

	~Node ( ) {
		if ( child == nullptr ) return;

		Node * ch = child;

		do {
			Node * tmp = ch->right;
			delete ch;
			ch = tmp;
		} while ( ch != child );
	}

	void rotateLeftBranch ( ) {
		if ( child != nullptr ) child->rotateLeftBranch ( );

		if ( rank > 1 ) {
			int angle = ext::random_devices::semirandom ( ) % rank;
			Node * newChild = child;

			for ( int i = 0; i < angle; i++ )
				newChild = newChild->right;

			child = newChild;
		}
	}

	void generateUnrankedSymbols ( const ext::vector < char > & alphabet ) {
		symbol = alphabet[ext::random_devices::semirandom ( ) % alphabet.size ( )];
		Node * nextChild = child;

		for ( int i = 0; i < rank; i++ ) {
			nextChild->generateUnrankedSymbols ( alphabet );
			nextChild = nextChild->right;
		}
	}

	void generateRankedSymbols ( const ext::map < int, ext::vector < char > > & rankedAlphabet ) {
		const ext::vector < char > & alphabet = rankedAlphabet.at ( rank );

		symbol = alphabet[ext::random_devices::semirandom ( ) % alphabet.size ( )];
		Node * nextChild = child;

		for ( int i = 0; i < rank; i++ ) {
			nextChild->generateRankedSymbols ( rankedAlphabet );
			nextChild = nextChild->right;
		}
	}

	void fillRanks ( ext::map < int, ext::vector < char > > & rankedAlphabet ) {
		rankedAlphabet[rank];
		Node * nextChild = child;

		for ( int i = 0; i < rank; i++ ) {
			nextChild->fillRanks ( rankedAlphabet );
			nextChild = nextChild->right;
		}
	}

	ext::tree < DefaultSymbolType > createUnrankedNode ( ) {
		ext::vector < ext::tree < DefaultSymbolType > > children;
		Node * nextChild = child;

		for ( int i = 0; i < rank; i++ ) {
			children.emplace_back ( ext::tree < DefaultSymbolType > ( nextChild->createUnrankedNode ( ) ) );
			nextChild = nextChild->right;
		}

		return ext::tree < DefaultSymbolType > ( DefaultSymbolType ( symbol ), std::move ( children ) );
	}

	ext::tree < DefaultSymbolType > createUnrankedPatternNode ( ) {
		if ( rank == 0 ) {
			return ext::tree < DefaultSymbolType > ( alphabet::WildcardSymbol::instance < DefaultSymbolType > ( ), { } );
		} else {
			ext::vector < ext::tree < DefaultSymbolType > > children;
			Node * nextChild = child;

			for ( int i = 0; i < rank; i++ ) {
				children.emplace_back ( ext::tree < DefaultSymbolType > ( nextChild->createUnrankedPatternNode ( ) ) );
				nextChild = nextChild->right;
			}

			return ext::tree < DefaultSymbolType > ( DefaultSymbolType ( symbol ), std::move ( children ) );
		}
	}

	ext::tree < common::ranked_symbol < > > createRankedNode ( ) {
		ext::vector < ext::tree < common::ranked_symbol < > > > children;
		Node * nextChild = child;

		for ( int i = 0; i < rank; i++ ) {
			children.emplace_back ( ext::tree < common::ranked_symbol < > > ( nextChild->createRankedNode ( ) ) );
			nextChild = nextChild->right;
		}

		return ext::tree < common::ranked_symbol < > > ( common::ranked_symbol < > ( DefaultSymbolType ( symbol ), rank ), std::move ( children ) );
	}

	ext::tree < common::ranked_symbol < > > createRankedPatternNode ( ) {
		if ( rank == 0 ) {
			return ext::tree < common::ranked_symbol < > > ( common::ranked_symbol < > ( alphabet::WildcardSymbol::instance < DefaultSymbolType > ( ), 0 ), { } );
		} else {
			ext::vector < ext::tree < common::ranked_symbol < > > > children;
			Node * nextChild = child;

			for ( int i = 0; i < rank; i++ ) {
				children.emplace_back ( ext::tree < common::ranked_symbol < > > ( nextChild->createRankedPatternNode ( ) ) );
				nextChild = nextChild->right;
			}

			return ext::tree < common::ranked_symbol < > > ( common::ranked_symbol < > ( DefaultSymbolType ( symbol ), rank ), std::move ( children ) );
		}
	}

	ext::tree < common::ranked_symbol < > > createRankedNonlinearPatternNode ( bool singleNonlinearVariable ) {
		if ( rank == 0 ) {
			if ( singleNonlinearVariable )
				return ext::tree < common::ranked_symbol < > > ( common::ranked_symbol < > ( DefaultSymbolType ( alphabet::NonlinearVariableSymbol < > ( DefaultSymbolType ( "A" ) ) ), 0 ), { } );
			else
				return ext::tree < common::ranked_symbol < > > ( common::ranked_symbol < > ( DefaultSymbolType ( alphabet::NonlinearVariableSymbol < > ( DefaultSymbolType ( symbol ) ) ), 0 ), { } );
		} else {
			ext::vector < ext::tree < common::ranked_symbol < > > > children;
			Node * nextChild = child;

			for ( int i = 0; i < rank; i++ ) {
				children.emplace_back ( ext::tree < common::ranked_symbol < > > ( nextChild->createRankedNonlinearPatternNode ( singleNonlinearVariable ) ) );
				nextChild = nextChild->right;
			}

			return ext::tree < common::ranked_symbol < > > ( common::ranked_symbol < > ( DefaultSymbolType ( symbol ), rank ), std::move ( children ) );
		}
	}

	void nicePrint ( std::ostream & os = common::Streams::out, const std::string & prefix = "", const bool last = true ) const {
		os << prefix;

		std::string nextPrefix ( prefix );

		if ( last ) {
			os << "\\-";
			nextPrefix += "  ";
		} else {
			os << "|-";
			nextPrefix += "| ";
		}

		os << symbol << " (" << rank << ")" << std::endl;

		Node * nextChild = child;

		for ( int i = 0; i < rank; i++ ) {
			 // os << nextPrefix << "|" << std::endl;
			nextChild->nicePrint ( os, nextPrefix, i == rank - 1 );
			nextChild = nextChild->right;
		}
	}

};

ext::vector < char > generateUnrankedAlphabet ( int maxAlphabetSize, bool randomizedAlphabet ) {
	ext::vector < char > symbols ( 26 );

	for ( int i = 0; i < 26; i++ ) symbols[i] = i + 'a';

	if ( randomizedAlphabet ) shuffle ( symbols.begin ( ), symbols.end ( ), ext::random_devices::semirandom );

	return ext::vector < char > ( symbols.begin ( ), symbols.begin ( ) + maxAlphabetSize );
}

void generateRankedAlphabet ( ext::map < int, ext::vector < char > > & rankedAlphabet, int maxAlphabetSize, bool randomizedAlphabet ) {
	int ranksCount = rankedAlphabet.size ( );
	ext::vector < char > unrankedAlphabet = generateUnrankedAlphabet ( maxAlphabetSize > ranksCount ? maxAlphabetSize : ranksCount, randomizedAlphabet );

	ext::set < int > rankSeparators;

	rankSeparators.insert ( 0 );
	rankSeparators.insert ( unrankedAlphabet.size ( ) );

	while ( ( int ) rankSeparators.size ( ) != ranksCount + 1 /*&& rankSeparators.size() != maxRank + 2*/ ) rankSeparators.insert ( ext::random_devices::semirandom ( ) % unrankedAlphabet.size ( ) );

	ext::set < int >::iterator it = rankSeparators.begin ( );

	for ( ext::map < int, ext::vector < char > >::iterator i = rankedAlphabet.begin ( ); i != rankedAlphabet.end ( ); ++i ) {
		ext::set < int >::iterator prevIt = it++;
		i->second.insert ( i->second.begin ( ), unrankedAlphabet.begin ( ) + * prevIt, unrankedAlphabet.begin ( ) + * it );
	}
}

Node * generateTreeStructure ( int depth, int nodesCount, int maxRank = INT_MAX ) {
	if ( depth >= nodesCount ) throw exception::CommonException ( "number of nodes is too small" );

	if ( ( maxRank != INT_MAX ) && ( pow ( maxRank, depth + 1 ) - 1 < nodesCount ) ) throw exception::CommonException ( "number of nodes is too small" );

	ext::vector < Node * > nodes ( nodesCount );

	 // generate path depth long
	Node * root = nodes[0] = new Node ( );

	for ( int i = 1; i <= depth; i++ ) nodes[i] = new Node ( nodes[i - 1] );

	 // move final leaf to end
	nodes[nodesCount - 1] = nodes[depth];

	int availableNodesIndex = depth;
	int finalNodesIndex = nodesCount - 2;

	while ( finalNodesIndex >= availableNodesIndex ) {
		int randomIndex = ext::random_devices::semirandom ( ) % availableNodesIndex;
		Node * parent = nodes[randomIndex];
		Node * node = new Node ( parent );

		 // put node to end if it reached depth limit
		if ( node->depth < depth ) {
			nodes[availableNodesIndex] = node;
			availableNodesIndex++;
		} else {
			nodes[finalNodesIndex] = node;
			finalNodesIndex--;
		}

		 // put parent node to end if it reached rank limit
		if ( parent->rank >= maxRank ) {
			nodes[randomIndex] = nodes[availableNodesIndex - 1];
			nodes[finalNodesIndex] = parent;
			availableNodesIndex--;
			finalNodesIndex--;
		}
	}

	 // move generated path to random branch
	root->rotateLeftBranch ( );

	return root;
}

UnrankedTree < > RandomUnrankedTreeFactory::generateUnrankedTree ( int depth, int nodesCount, int maxAlphabetSize, bool randomizedAlphabet, int maxRank ) {
	Node * root = generateTreeStructure ( depth, nodesCount, maxRank );
	ext::vector < char > alphabet = generateUnrankedAlphabet ( maxAlphabetSize, randomizedAlphabet );

	root->generateUnrankedSymbols ( alphabet );

	ext::set < DefaultSymbolType > treeAlphabet;

	for ( char it : alphabet )
		treeAlphabet.insert ( DefaultSymbolType ( it ) );

	UnrankedTree < > tree ( treeAlphabet, root->createUnrankedNode ( ) );
	delete root;
	return tree;
}

auto GenerateUnrankedTree = registration::AbstractRegister < RandomUnrankedTreeFactory, tree::UnrankedTree < >, int, int, int, bool, int > ( RandomUnrankedTreeFactory::generateUnrankedTree, abstraction::AlgorithmCategories::AlgorithmCategory::DEFAULT, "depth", "nodesCount", "maxAlphabetSize", "randomizedAlphabet", "maxRank" );

UnrankedPattern < > RandomUnrankedPatternFactory::generateUnrankedPattern ( int depth, int nodesCount, int maxAlphabetSize, bool randomizedAlphabet, int maxRank ) {
	Node * root = generateTreeStructure ( depth, nodesCount, maxRank );
	ext::vector < char > alphabet = generateUnrankedAlphabet ( maxAlphabetSize, randomizedAlphabet );

	root->generateUnrankedSymbols ( alphabet );

	ext::set < DefaultSymbolType > treeAlphabet;

	for ( ext::vector < char >::const_iterator it = alphabet.begin ( ); it != alphabet.end ( ); ++it )
		treeAlphabet.insert ( DefaultSymbolType ( * it ) );

	DefaultSymbolType subtreeWildcard = alphabet::WildcardSymbol::instance < DefaultSymbolType > ( );
	treeAlphabet.insert ( subtreeWildcard );
	UnrankedPattern < > tree ( std::move ( subtreeWildcard ), treeAlphabet, root->createUnrankedPatternNode ( ) );
	delete root;
	return tree;
}

auto GenerateUnrankedPattern = registration::AbstractRegister < RandomUnrankedPatternFactory, tree::UnrankedPattern < >, int, int, int, bool, int > ( RandomUnrankedPatternFactory::generateUnrankedPattern, abstraction::AlgorithmCategories::AlgorithmCategory::DEFAULT, "depth", "nodesCount", "maxAlphabetSize", "randomizedAlphabet", "maxRank" );

RankedTree < > RandomRankedTreeFactory::generateRankedTree ( int depth, int nodesCount, int maxAlphabetSize, bool randomizedAlphabet, int maxRank ) {
	Node * root = generateTreeStructure ( depth, nodesCount, maxRank );
	ext::map < int, ext::vector < char > > rankedAlphabet;

	root->fillRanks ( rankedAlphabet );
	generateRankedAlphabet ( rankedAlphabet, maxAlphabetSize, randomizedAlphabet );
	root->generateRankedSymbols ( rankedAlphabet );

	ext::set < common::ranked_symbol < > > treeRankedAlphabet;

	for ( const std::pair < const int, ext::vector < char > > & it : rankedAlphabet )
		for ( char i : it.second )
			treeRankedAlphabet.insert ( common::ranked_symbol < > ( DefaultSymbolType ( i ), it.first ) );

	RankedTree < > tree ( treeRankedAlphabet, root->createRankedNode ( ) );
	delete root;
	return tree;
}

auto GenerateRankedTree = registration::AbstractRegister < RandomRankedTreeFactory, tree::RankedTree < >, int, int, int, bool, int > ( RandomRankedTreeFactory::generateRankedTree, abstraction::AlgorithmCategories::AlgorithmCategory::DEFAULT, "depth", "nodesCount", "maxAlphabetSize", "randomizedAlphabet", "maxRank" );

RankedPattern < > RandomRankedPatternFactory::generateRankedPattern ( int depth, int nodesCount, int maxAlphabetSize, bool randomizedAlphabet, int maxRank ) {
	Node * root = generateTreeStructure ( depth, nodesCount, maxRank );
	ext::map < int, ext::vector < char > > rankedAlphabet;

	root->fillRanks ( rankedAlphabet );
	generateRankedAlphabet ( rankedAlphabet, maxAlphabetSize, randomizedAlphabet );
	root->generateRankedSymbols ( rankedAlphabet );

	ext::set < common::ranked_symbol < > > treeRankedAlphabet;

	for ( const std::pair < const int, ext::vector < char > > & it : rankedAlphabet )
		for ( char i : it.second )
			treeRankedAlphabet.insert ( common::ranked_symbol < > ( DefaultSymbolType ( i ), it.first ) );

	common::ranked_symbol < > subtreeWildcard = alphabet::WildcardSymbol::instance < common::ranked_symbol < > > ( );
	treeRankedAlphabet.insert ( subtreeWildcard );
	RankedPattern < > tree ( std::move ( subtreeWildcard ), treeRankedAlphabet, root->createRankedPatternNode ( ) );
	delete root;
	return tree;
}

auto GenerateRankedPattern = registration::AbstractRegister < RandomRankedPatternFactory, tree::RankedPattern < >, int, int, int, bool, int > ( RandomRankedPatternFactory::generateRankedPattern, abstraction::AlgorithmCategories::AlgorithmCategory::DEFAULT, "depth", "nodesCount", "randomizedAlphabet", "maxAlphabetSize", "maxRank" );

RankedNonlinearPattern < > RandomRankedNonlinearPatternFactory::generateRankedNonlinearPattern ( int depth, int nodesCount, int maxAlphabetSize, bool randomizedAlphabet, bool singleNonlinearVariable, int maxRank ) {
	Node * root = generateTreeStructure ( depth, nodesCount, maxRank );
	ext::map < int, ext::vector < char > > rankedAlphabet;

	root->fillRanks ( rankedAlphabet );
	generateRankedAlphabet ( rankedAlphabet, maxAlphabetSize, randomizedAlphabet );
	root->generateRankedSymbols ( rankedAlphabet );

	ext::set < common::ranked_symbol < > > treeRankedAlphabet;
	ext::set < common::ranked_symbol < > > nonlinearVariables;

	for ( const std::pair < const int, ext::vector < char > > & it : rankedAlphabet )
		for ( char i : it.second )
			treeRankedAlphabet.insert ( common::ranked_symbol < > ( DefaultSymbolType ( i ), it.first ) );

	if ( singleNonlinearVariable )
		nonlinearVariables.insert ( common::ranked_symbol < > ( DefaultSymbolType ( alphabet::NonlinearVariableSymbol < > ( DefaultSymbolType ( "A" ) ) ), 0 ) );
	else
		for ( char i : rankedAlphabet [ 0 ] )
			nonlinearVariables.insert ( common::ranked_symbol < > ( DefaultSymbolType ( alphabet::NonlinearVariableSymbol < > ( DefaultSymbolType ( i ) ) ), 0 ) );

	common::ranked_symbol < > subtreeWildcard = alphabet::WildcardSymbol::instance < common::ranked_symbol < > > ( );
	treeRankedAlphabet.insert ( subtreeWildcard );
	treeRankedAlphabet.insert ( nonlinearVariables.begin ( ), nonlinearVariables.end ( ) );
	RankedNonlinearPattern < > tree ( std::move ( subtreeWildcard ), nonlinearVariables, treeRankedAlphabet, root->createRankedNonlinearPatternNode ( singleNonlinearVariable ) );
	delete root;
	return tree;
}

} /* namespace generate */

} /* namespace tree */

namespace {

auto GenerateRankedNonlinearPattern = registration::AbstractRegister < tree::generate::RandomRankedNonlinearPatternFactory, tree::RankedNonlinearPattern < >, int, int, int, bool, bool, int > ( tree::generate::RandomRankedNonlinearPatternFactory::generateRankedNonlinearPattern, abstraction::AlgorithmCategories::AlgorithmCategory::DEFAULT, "depth", "nodesCount", "maxAlphabetSize", "randomizedAlphabet", "singleNonlinearVariable", "maxRank" );

} /* namespace */
