/*
 * algorithms.h
 *
 *  Created on: 07. 04. 2021
 *      Author: Martin Prokopic
 */

#pragma once

#include <alib/measure>
#include <string/LinearStringTerminatingSymbolBWT.h>
#include <stringology/transform/BWTDecodeNaive.h>
#include <stringology/exact/BoyerMoore.h>
#include <stringology/exact/BWTBoyerMoore.h>
#include <stringology/exact/BWTBinarySearch.h>
#include <stringology/indexing/SuffixArrayFromBWTNaive.h>
#include <stringology/query/SuffixArrayFactors.h>
#include <indexes/stringology/SimpleFMIndex.h>
#include <stringology/query/FMIndexCount.h>
#include <stringology/query/FMIndexFactors.h>
#include "helpers.h"

namespace algorithms {

template < class SymbolType, class ForwardIt >
void measureDecodeBoyerMoore ( const string::LinearStringTerminatingSymbolBWT < SymbolType > & bwt, ForwardIt start,
                               ForwardIt end, ext::vector < object::Object > info ) {
    measurements::reset ( );
    measurements::start ( "Overall", measurements::Type::OVERALL );
    auto data = stringology::indexing::BWTAuxiliaryDataConstruct::construct ( bwt,
                                                                              indexes::stringology::BWTAuxiliaryDataType::M_OCC_PREFIX_SUM );
    measurements::start ( "BWTDecodeNaiveReal", measurements::Type::ALGORITHM );
    auto decoded = stringology::transform::BWTDecodeNaive::decode ( bwt, data );
    measurements::end ( );
    measurements::start ( "BoyerMooreOverall", measurements::Type::ALGORITHM );
    unsigned matches = 0;
    while ( start != end )
        matches += stringology::exact::BoyerMoore::match ( decoded, *( start ++ )).size ( );
    measurements::end ( );
    measurements::end ( );

    info.emplace_back ( "DecodeBoyerMoore" );
    info.emplace_back ( matches );

    info.emplace_back ( "Overall" );
    helpers::writeResults ( "Overall", info );
    info.back ( ) = object::Object ( "BWTAuxiliaryDataConstruct" );
    helpers::writeResults ( "BWTAuxiliaryDataConstruct", info );
    info.back ( ) = object::Object ( "BWTDecodeNaive" );
    helpers::writeResults ( "BWTDecodeNaiveReal", info );
    info.back ( ) = object::Object ( "BCS+GSS+BoyerMoore" );
    helpers::writeResults ( "BoyerMooreOverall", info );

    info.back ( ) = object::Object ( "BCS+GSS" );
    helpers::writeResults ( "Preprocess", info );
    info.back ( ) = object::Object ( "BoyerMoore" );
    helpers::writeResults ( "Algorithm", info );
}

template < class SymbolType, class ForwardIt >
void measureBWTBoyerMoore ( const string::LinearStringTerminatingSymbolBWT < SymbolType > & bwt, ForwardIt start,
                            ForwardIt end, ext::vector < object::Object > info ) {
    measurements::reset ( );
    measurements::start ( "Overall", measurements::Type::OVERALL );
    auto data = stringology::indexing::BWTAuxiliaryDataConstruct::construct ( bwt,
                                                                              indexes::stringology::BWTAuxiliaryDataType::HR_TEXT_TO_FIRST );
    measurements::start ( "BWTBoyerMooreOverall", measurements::Type::ALGORITHM );
    unsigned matches = 0;
    while ( start != end )
        matches += stringology::exact::BWTBoyerMoore::match ( bwt, *( start ++ ), data ).size ( );
    measurements::end ( );
    measurements::end ( );

    info.emplace_back ( "BWTBoyerMoore" );
    info.emplace_back ( matches );

    info.emplace_back ( "Overall" );
    helpers::writeResults ( "Overall", info );
    info.back ( ) = object::Object ( "BWTAuxiliaryDataConstruct" );
    helpers::writeResults ( "BWTAuxiliaryDataConstruct", info );
    info.back ( ) = object::Object ( "BCS+GSS+BWTBoyerMoore" );
    helpers::writeResults ( "BWTBoyerMooreOverall", info );

    info.back ( ) = object::Object ( "BCS+GSS" );
    helpers::writeResults ( "BCS & GSS construction", info );
    info.back ( ) = object::Object ( "BWTBoyerMoore" );
    helpers::writeResults ( "BWTBoyerMoore", info );
}

template < class SymbolType, class ForwardIt >
void measureBWTBinarySearch ( const string::LinearStringTerminatingSymbolBWT < SymbolType > & bwt, ForwardIt start,
                              ForwardIt end, ext::vector < object::Object > info ) {
    measurements::reset ( );
    measurements::start ( "Overall", measurements::Type::OVERALL );
    auto data = stringology::indexing::BWTAuxiliaryDataConstruct::construct ( bwt,
                                                                              indexes::stringology::BWTAuxiliaryDataType::M_OCC_PREFIX_SUM |
                                                                              indexes::stringology::BWTAuxiliaryDataType::W_FORWARD_DECODE |
                                                                              indexes::stringology::BWTAuxiliaryDataType::I_FIRST_TO_TEXT );
    measurements::start ( "BWTBinarySearchOverall", measurements::Type::ALGORITHM );
    unsigned matches = 0;
    while ( start != end )
        matches += stringology::exact::BWTBinarySearch::match ( bwt, *( start ++ ), data ).size ( );
    measurements::end ( );
    measurements::end ( );

    info.emplace_back ( "BWTBinarySearch" );
    info.emplace_back ( matches );

    info.emplace_back ( "Overall" );
    helpers::writeResults ( "Overall", info );
    info.back ( ) = object::Object ( "BWTAuxiliaryDataConstruct" );
    helpers::writeResults ( "BWTAuxiliaryDataConstruct", info );
    info.back ( ) = object::Object ( "BWTBinarySearch" );
    helpers::writeResults ( "BWTBinarySearchOverall", info );
}

template < class SymbolType, class ForwardIt >
void measureBWTSuffixArray ( const string::LinearStringTerminatingSymbolBWT < SymbolType > & bwt, ForwardIt start,
                             ForwardIt end, ext::vector < object::Object > info ) {
    measurements::reset ( );
    measurements::start ( "Overall", measurements::Type::OVERALL );
    auto data = stringology::indexing::BWTAuxiliaryDataConstruct::construct ( bwt,
                                                                              indexes::stringology::BWTAuxiliaryDataType::M_OCC_PREFIX_SUM );
    measurements::start ( "SuffixArrayFromBWTNaiveReal", measurements::Type::ALGORITHM );
    auto sa = stringology::indexing::SuffixArrayFromBWTNaive::construct ( bwt, data );
    measurements::end ( );
    measurements::start ( "SuffixArrayFactorsOverall", measurements::Type::ALGORITHM );
    unsigned matches = 0;
    while ( start != end )
        matches += stringology::query::SuffixArrayFactors::query ( sa, *( start ++ )).size ( );
    measurements::end ( );
    measurements::end ( );

    info.emplace_back ( "BWTSuffixArray" );
    info.emplace_back ( matches );

    info.emplace_back ( "Overall" );
    helpers::writeResults ( "Overall", info );
    info.back ( ) = object::Object ( "BWTAuxiliaryDataConstruct" );
    helpers::writeResults ( "BWTAuxiliaryDataConstruct", info );
    info.back ( ) = object::Object ( "SuffixArrayFromBWTNaive" );
    helpers::writeResults ( "SuffixArrayFromBWTNaiveReal", info );
    info.back ( ) = object::Object ( "SuffixArrayFactors" );
    helpers::writeResults ( "SuffixArrayFactorsOverall", info );
}

template < class SymbolType, class ForwardIt >
void measureFMIndexCount ( const indexes::stringology::SimpleFMIndex < SymbolType > & fmIndex, ForwardIt start,
                           ForwardIt end, ext::vector < object::Object > info ) {
    measurements::reset ( );
    measurements::start ( "Overall", measurements::Type::OVERALL );
    unsigned matches = 0;
    while ( start != end )
        matches += stringology::query::FMIndexCount::count ( fmIndex, *( start ++ ));
    measurements::end ( );

    info.emplace_back ( "FMIndexCount" );
    info.emplace_back ( matches );

    info.emplace_back ( "Overall" );
    helpers::writeResults ( "Overall", info );
}

template < class SymbolType, class ForwardIt >
void measureFMIndexFactors ( const indexes::stringology::SimpleFMIndex < SymbolType > & fmIndex, ForwardIt start,
                             ForwardIt end, ext::vector < object::Object > info ) {
    measurements::reset ( );
    measurements::start ( "Overall", measurements::Type::OVERALL );
    unsigned matches = 0;
    while ( start != end )
        matches += stringology::query::FMIndexFactors::match ( fmIndex, *( start ++ )).size ( );
    measurements::end ( );

    info.emplace_back ( "FMIndexFactors" );
    info.emplace_back ( matches );

    info.emplace_back ( "Overall" );
    helpers::writeResults ( "Overall", info );
}

} /* namespace algorithms */