/*
 * FMIndexCommonTest.cpp
 *
 *  Created on: 21. 02. 2021
 *      Author: Martin Prokopic
 */

#include <catch2/catch.hpp>

#include <string/String.h>
#include <string/LinearStringTerminatingSymbolBWT.h>
#include <stringology/indexing/SimpleFMIndexConstruct.h>
#include <stringology/common/FMIndexCommon.h>

using string::LinearString;
using string::LinearStringTerminatingSymbolBWT;
using stringology::indexing::SimpleFMIndexConstruct;
using stringology::indexing::BWTAuxiliaryDataConstruct;
using indexes::stringology::BWTAuxiliaryDataType;
using stringology::common::FMIndexCommon;

TEST_CASE ( "FM Index Common", "[unit][stringology][common][fmindex][bwt]" ) {

    unsigned len = 7;
    LinearStringTerminatingSymbolBWT < char > input ( '$', string::stringFrom ( "banana$" ));

    SECTION ( "occ function" ) {
        unsigned superBucketSize = GENERATE( 4, 2 );
        INFO( "superBucketSize = " + std::to_string ( superBucketSize ));

        auto index = SimpleFMIndexConstruct::construct ( input, superBucketSize, 2, 0 );

        ext::vector < unsigned > occ ( len );
        for ( unsigned i = 0; i < len; i ++ )
            occ[ i ] = FMIndexCommon::occ ( index, 'b', i );
        REQUIRE ( occ == ext::vector < unsigned >{ 1, 1, 1, 1, 1, 1, 1 } );

        for ( unsigned i = 0; i < len; i ++ )
            occ[ i ] = FMIndexCommon::occ ( index, 'a', i );
        REQUIRE ( occ == ext::vector < unsigned >{ 0, 1, 1, 2, 2, 3, 3 } );

        for ( unsigned i = 0; i < len; i ++ )
            occ[ i ] = FMIndexCommon::occ ( index, 'n', i );
        REQUIRE ( occ == ext::vector < unsigned >{ 0, 0, 1, 1, 2, 2, 2 } );

        for ( unsigned i = 0; i < len; i ++ )
            occ[ i ] = FMIndexCommon::occ ( index, '$', i );
        REQUIRE ( occ == ext::vector < unsigned >{ 0, 0, 0, 0, 0, 0, 1 } );
    }

    SECTION ( "m function" ) {
        auto index = SimpleFMIndexConstruct::construct ( input, 4, 2, 0 );

        REQUIRE( FMIndexCommon::m ( index, '$' ) == 0 );
        REQUIRE( FMIndexCommon::m ( index, 'a' ) == 1 );
        REQUIRE( FMIndexCommon::m ( index, 'b' ) == 4 );
        REQUIRE( FMIndexCommon::m ( index, 'n' ) == 5 );
    }

    SECTION ( "mNext function" ) {
        auto index = SimpleFMIndexConstruct::construct ( input, 4, 2, 0 );

        REQUIRE( FMIndexCommon::mNext ( index, '$' ) == 1 );
        REQUIRE( FMIndexCommon::mNext ( index, 'a' ) == 4 );
        REQUIRE( FMIndexCommon::mNext ( index, 'b' ) == 5 );
        REQUIRE( FMIndexCommon::mNext ( index, 'n' ) == 7 );
    }

    SECTION ( "isMarked and pos functions" ) {
        LinearStringTerminatingSymbolBWT < char > input2 ( '$', string::stringFrom ( "aaabbbb$" ));

        auto data = BWTAuxiliaryDataConstruct::construct ( input2, BWTAuxiliaryDataType::M_OCC_PREFIX_SUM );
        data.setIFirstToText ( ext::vector < unsigned >{ 100, 101, 102, 103, 104, 105, 106, 107 } );
        data.setDataTypePresent ( BWTAuxiliaryDataType::I_FIRST_TO_TEXT );

        auto index = SimpleFMIndexConstruct::construct ( input2, 4, 2, 50, data );

        REQUIRE( index.getMarkedSymbol ( ) == 'b' );

        SECTION ( "isMarked function" ) {
            ext::vector < bool > marked ( 8 );
            for ( unsigned i = 0; i < 8; i ++ )
                marked[ i ] = FMIndexCommon::isMarked ( index, i );
            REQUIRE( marked == ext::vector < bool >{ false, false, false, true, true, true, true, false } );
        }

        SECTION ( "pos function" ) {
            REQUIRE( FMIndexCommon::pos ( index, 3 ) == 103 );
            REQUIRE( FMIndexCommon::pos ( index, 4 ) == 104 );
            REQUIRE( FMIndexCommon::pos ( index, 5 ) == 105 );
            REQUIRE( FMIndexCommon::pos ( index, 6 ) == 106 );
        }
    }

}
