/*
 * NonlinearCompressedBitParallelTreeIndex.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: 22. 8. 2017
 *      Author: Jan Travnicek
 */

#ifndef ARBOLOGY_NONLINEAR_COMPRESSED_BIT_PARALLEL_INDEX_H_
#define ARBOLOGY_NONLINEAR_COMPRESSED_BIT_PARALLEL_INDEX_H_

#include <alib/set>
#include <alib/map>
#include <alib/string>
#include <alib/compare>
#include <sstream>

#include <common/ranked_symbol.hpp>

#include <core/components.hpp>
#include <exception/CommonException.h>

#include <common/SparseBoolVector.hpp>

#include <alphabet/common/SymbolNormalize.h>

namespace indexes {

namespace arbology {

class GeneralAlphabet;

/**
 * \brief Compressed bit parallel nonlinear tree index. Stores a bit vector for each symbol of the alphabet. The bit vector of symbol a contains true on index i if symbol a is on i-th position in the indexed string. The class does not check whether the bit vectors actually represent valid index. The bit vectors are compressed with run length encoding packing runs of false values.
 * Additionally the index contains a subtree jump table representing positions after a jump over a subtree.
 * The index is supposed to be able to provide data to search for nonlinear tree patterns. Therefore it also contains representation of subtree repeats.
 *
 * The actual notation of used tree is irelevant. The index, as fas as the data structure is concerned, is not different. Of course tree in postfix notation must be queried with patterns in postfix notation, etc.
 *
 * \tparam SymbolType used for the symbol part of the ranked symbol
 */
template < class SymbolType = DefaultSymbolType >
class NonlinearCompressedBitParallelTreeIndex final : public ext::CompareOperators < NonlinearCompressedBitParallelTreeIndex < SymbolType > >, public core::Components < NonlinearCompressedBitParallelTreeIndex < SymbolType >, ext::set < common::ranked_symbol < SymbolType > >, component::Set, GeneralAlphabet > {
	/**
	 * Representation of compressed bit vectors for each symbol of the alphabet.
	 */
	ext::map < common::ranked_symbol < SymbolType >, common::SparseBoolVector > m_vectors;

	/**
	 * Representation of the subtree jump table.
	 */
	ext::vector < int > m_jumpTable;

	/**
	 * Representation of repeats of subtrees in the indexed tree.
	 */
	ext::vector < unsigned > m_repeats;

public:
	/**
	 * Creates a new instance of the index with concrete alphabet, bit vectors, subtree jump table, and subtree repeats.
	 *
	 * \param alphabet the alphabet of indexed string
	 * \param vectors the compressed bit vectors
	 * \param jumpTable the subtree jump table
	 * \param repeats the subtree repeats table
	 */
	explicit NonlinearCompressedBitParallelTreeIndex ( ext::set < common::ranked_symbol < SymbolType > > alphabet, ext::map < common::ranked_symbol < SymbolType >, common::SparseBoolVector > vectors, ext::vector < int > jumpTable, ext::vector < unsigned > repeats );

	/**
	 * Getter of the compressed bit vectors.
	 *
	 * @return compressed bit vectors
	 */
	const ext::map < common::ranked_symbol < SymbolType >, common::SparseBoolVector > & getData ( ) const &;

	/**
	 * Getter of the compressed bit vectors.
	 *
	 * @return compressed bit vectors
	 */
	ext::map < common::ranked_symbol < SymbolType >, common::SparseBoolVector > && getData ( ) &&;

	/**
	 * Getter of the subtree jump table
	 *
	 * @return the subtree jump table
	 */
	const ext::vector < int > & getJumps ( ) const &;

	/**
	 * Getter of the subtree jump table
	 *
	 * @return the subtree jump table
	 */
	ext::vector < int > && getJumps ( ) &&;

	/**
	 * Getter of the subtree repeats table
	 *
	 * @return the subtree repeats table
	 */
	const ext::vector < unsigned > & getRepeats ( ) const &;

	/**
	 * Getter of the subtree repeats table
	 *
	 * @return the subtree repeats table
	 */
	ext::vector < unsigned > && getRepeats ( ) &&;

	/**
	 * Reconstructs the indexed string from bit vectors.
	 *
	 * @return the original indexed string
	 */
	ext::vector < common::ranked_symbol < SymbolType > > getString ( ) const;

	/**
	 * Getter of the alphabet of the indexed tree.
	 *
	 * \returns the alphabet of the indexed tree
	 */
	const ext::set < common::ranked_symbol < SymbolType > > & getAlphabet ( ) const & {
		return this->template accessComponent < GeneralAlphabet > ( ).get ( );
	}

	/**
	 * Getter of the alphabet of the indexed tree.
	 *
	 * \returns the alphabet of the indexed tree
	 */
	ext::set < common::ranked_symbol < SymbolType > > && getAlphabet ( ) && {
		return std::move ( this->template accessComponent < GeneralAlphabet > ( ).get ( ) );
	}

	/**
	 * Changes the bit vector for concrete symbol.
	 *
	 * \param symbol the changed symbol
	 * \param data the new bit vector
	 */
	void setNonlinearCompressedBitVectorForSymbol ( common::ranked_symbol < SymbolType > symbol, common::SparseBoolVector data );

	/**
	 * Remover of a symbol from the alphabet. The symbol can be removed if it is not used in any of bit vector keys.
	 *
	 * \param symbol a symbol to remove.
	 */
	bool removeSymbolFromAlphabet ( const common::ranked_symbol < SymbolType > & symbol ) {
		return this->template accessComponent < GeneralAlphabet > ( ).remove ( symbol );
	}

	/**
	 * The actual compare method.
	 *
	 * \param other the other instance
	 *
	 * \returns the actual relation between two by type same index instances
	 */
	int compare ( const NonlinearCompressedBitParallelTreeIndex & other ) const;

	/**
	 * Print this object as raw representation to ostream.
	 *
	 * \param out ostream where to print
	 * \param instance object to print
	 *
	 * \returns modified output stream
	 */
	friend std::ostream & operator << ( std::ostream & out, const NonlinearCompressedBitParallelTreeIndex & instance ) {
		return out << "(NonlinearCompressedBitParallelTreeIndex " << instance.m_vectors << ", " << instance.m_jumpTable << ")";
	}

	/**
	 * Casts this instance to as compact as possible string representation.
	 *
	 * \returns string representation of the object
	 */
	explicit operator std::string ( ) const;
};

} /* namespace arbology */

} /* namespace indexes */

namespace indexes {

namespace arbology {

template < class SymbolType >
NonlinearCompressedBitParallelTreeIndex < SymbolType >::NonlinearCompressedBitParallelTreeIndex ( ext::set < common::ranked_symbol < SymbolType > > alphabet, ext::map < common::ranked_symbol < SymbolType >, common::SparseBoolVector > vectors, ext::vector < int > jumpTable, ext::vector < unsigned > repeats ) : core::Components < NonlinearCompressedBitParallelTreeIndex, ext::set < common::ranked_symbol < SymbolType > >, component::Set, GeneralAlphabet > ( std::move ( alphabet ) ), m_vectors ( std::move ( vectors ) ), m_jumpTable ( std::move ( jumpTable ) ), m_repeats ( std::move ( repeats ) ) {
}

template < class SymbolType >
const ext::map < common::ranked_symbol < SymbolType >, common::SparseBoolVector > & NonlinearCompressedBitParallelTreeIndex < SymbolType >::getData ( ) const & {
	return m_vectors;
}

template < class SymbolType >
ext::map < common::ranked_symbol < SymbolType >, common::SparseBoolVector > && NonlinearCompressedBitParallelTreeIndex < SymbolType >::getData ( ) && {
	return std::move ( m_vectors );
}

template < class SymbolType >
const ext::vector < int > & NonlinearCompressedBitParallelTreeIndex < SymbolType >::getJumps ( ) const & {
	return m_jumpTable;
}

template < class SymbolType >
ext::vector < int > && NonlinearCompressedBitParallelTreeIndex < SymbolType >::getJumps ( ) && {
	return std::move ( m_jumpTable );
}

template < class SymbolType >
const ext::vector < unsigned > & NonlinearCompressedBitParallelTreeIndex < SymbolType >::getRepeats ( ) const & {
	return m_repeats;
}

template < class SymbolType >
ext::vector < unsigned > && NonlinearCompressedBitParallelTreeIndex < SymbolType >::getRepeats ( ) && {
	return std::move ( m_repeats );
}

template < class SymbolType >
ext::vector < common::ranked_symbol < SymbolType > > NonlinearCompressedBitParallelTreeIndex < SymbolType >::getString ( ) const {
	ext::vector < common::ranked_symbol < SymbolType > > res;

	unsigned index = 0;

	do {
		for ( const std::pair < const common::ranked_symbol < SymbolType >, common::SparseBoolVector > & nonlinearcompressedBitVector : m_vectors )
			if ( nonlinearcompressedBitVector.second.size ( ) > index && nonlinearcompressedBitVector.second [ index ] ) {
				res.push_back ( nonlinearcompressedBitVector.first );
				continue;
			}

	} while ( res.size ( ) == index ++ + 1 );

	return res;
}

template < class SymbolType >
void NonlinearCompressedBitParallelTreeIndex < SymbolType >::setNonlinearCompressedBitVectorForSymbol ( common::ranked_symbol < SymbolType > symbol, common::SparseBoolVector data ) {
	this->m_vectors [ symbol ] = std::move ( data );
}

template < class SymbolType >
int NonlinearCompressedBitParallelTreeIndex < SymbolType >::compare ( const NonlinearCompressedBitParallelTreeIndex & other ) const {
	auto first = ext::tie ( getData ( ), getAlphabet ( ), getJumps ( ), getRepeats ( ) );
	auto second = ext::tie ( other.getData ( ), other.getAlphabet ( ), other.getJumps ( ), getRepeats ( ) );

	static ext::compare < decltype ( first ) > comp;

	return comp ( first, second );
}

template < class SymbolType >
NonlinearCompressedBitParallelTreeIndex < SymbolType >::operator std::string ( ) const {
	std::stringstream ss;
	ss << * this;
	return ss.str ( );
}

} /* namespace arbology */

} /* namespace indexes */

namespace core {

/**
 * Helper class specifying constraints for the internal alphabet component of the index.
 *
 * \tparam SymbolType used for the symbol part of the ranked symbol
 */
template < class SymbolType >
class SetConstraint < indexes::arbology::NonlinearCompressedBitParallelTreeIndex < SymbolType >, common::ranked_symbol < SymbolType >, indexes::arbology::GeneralAlphabet > {
public:
	/**
	 * Returns true if the symbol is still used as key in mapping symbol to bit vector.
	 *
	 * \param index the tested index
	 * \param symbol the tested symbol
	 *
	 * \returns true if the symbol is used, false othervise
	 */
	static bool used ( const indexes::arbology::NonlinearCompressedBitParallelTreeIndex < SymbolType > & index, const common::ranked_symbol < SymbolType > & symbol ) {
		const ext::map < common::ranked_symbol < SymbolType >, common::SparseBoolVector > & content = index.getData ( );

		return content.find ( symbol ) != content.end ( );
	}

	/**
	 * Returns true as all symbols are possibly available to be elements of the alphabet.
	 *
	 * \param index the tested index
	 * \param symbol the tested symbol
	 *
	 * \returns true
	 */
	static bool available ( const indexes::arbology::NonlinearCompressedBitParallelTreeIndex < SymbolType > &, const common::ranked_symbol < SymbolType > & ) {
		return true;
	}

	/**
	 * All symbols are valid as symbols of the alphabet.
	 *
	 * \param index the tested index
	 * \param symbol the tested symbol
	 */
	static void valid ( const indexes::arbology::NonlinearCompressedBitParallelTreeIndex < SymbolType > &, const common::ranked_symbol < SymbolType > & ) {
	}

};

/**
 * Helper for normalisation of types specified by templates used as internal datatypes of symbols.
 *
 * \returns new instance of the index with default template parameters or unmodified instance if the template parameters were already the default ones
 */
template < class SymbolType >
struct normalize < indexes::arbology::NonlinearCompressedBitParallelTreeIndex < SymbolType > > {
	static indexes::arbology::NonlinearCompressedBitParallelTreeIndex < > eval ( indexes::arbology::NonlinearCompressedBitParallelTreeIndex < SymbolType > && value ) {
		ext::set < common::ranked_symbol < DefaultSymbolType > > alphabet = alphabet::SymbolNormalize::normalizeRankedAlphabet ( std::move ( value ).getAlphabet ( ) );

		ext::map < common::ranked_symbol < DefaultSymbolType >, common::SparseBoolVector > vectors;
		for ( std::pair < common::ranked_symbol < SymbolType >, common::SparseBoolVector > && vector : ext::make_mover ( std::move ( value ).getData ( ) ) )
			vectors.insert ( std::make_pair ( alphabet::SymbolNormalize::normalizeRankedSymbol ( std::move ( vector.first ) ), std::move ( vector.second ) ) );

		return indexes::arbology::NonlinearCompressedBitParallelTreeIndex < > ( std::move ( alphabet ), std::move ( vectors ), std::move ( value ).getJumps ( ), std::move ( value ).getRepeats ( ) );
	}
};

} /* namespace core */

#endif /* ARBOLOGY_NONLINEAR_COMPRESSED_BIT_PARALLEL_INDEX_H_ */
