/*
 * BWTNaiveTest.cpp
 *
 *  Created on: 15. 02. 2021
 *      Author: Martin Prokopic
 */

#include <catch2/catch.hpp>

#include <stringology/transform/BWTNaive.h>
#include <stringology/transform/BWTFromSuffixArrayNaive.h>
#include <string/String.h>
#include <stringology/indexing/SuffixArrayNaive.h>

using string::LinearString;
using string::LinearStringTerminatingSymbolBWT;
using stringology::indexing::SuffixArrayNaive;
using stringology::transform::BWTNaive;
using stringology::transform::BWTFromSuffixArrayNaive;

/** Compare string content, taking care of possible different terminating symbols */
template < class SymbolType >
bool compareStringContent ( const string::LinearStringTerminatingSymbolBWT < SymbolType > a,
                            const string::LinearStringTerminatingSymbolBWT < SymbolType > b ) {
    if ( a.size ( ) != b.size ( ))
        return false;

    for ( size_t i = 0; i < a.size ( ); i ++ ) {
        bool isATerminatingSymbol = a.getContent ( )[ i ] == a.getTerminatingSymbol ( );
        bool isBTerminatingSymbol = b.getContent ( )[ i ] == b.getTerminatingSymbol ( );

        if ( isATerminatingSymbol != isBTerminatingSymbol )
            return false;

        if ( ! isATerminatingSymbol && a.getContent ( )[ i ] != b.getContent ( )[ i ] )
            return false;
    }

    return true;
}

TEST_CASE ( "BWT Naive", "[unit][stringology][bwt]" ) {

    SECTION ( "basic test" ) {
        auto testcase = GENERATE (
                std::make_pair ( "banana", "annb$aa" ),
                std::make_pair ( "mississippi", "ipssm$pissii" ),
        // test wrap around issues (terminating symbol must be minimal)
                std::make_pair ( "sississippi", "ipssspiss$ii" ),
                std::make_pair ( "\x01sississippi", "i$pssspiss\x01ii" ),
        // empty string
                std::make_pair ( "", "$" ));

        LinearString < char > original = string::stringFrom ( testcase.first );
        LinearStringTerminatingSymbolBWT < char > reference ( '$', string::stringFrom ( testcase.second ));

        SECTION ( "test on chars" ) {
            SECTION ( "BWTNaive::construct" ) {
                LinearStringTerminatingSymbolBWT < char > transformed = BWTNaive::construct ( original );

                INFO( "transformed: " << transformed << "; reference: " << reference );
                REQUIRE ( compareStringContent ( transformed, reference ));
            }

            SECTION ( "BWTFromSuffixArray::construct (via SuffixArrayNaive::construct)" ) {
                auto sa = stringology::indexing::SuffixArrayNaive::construct ( original );
                LinearStringTerminatingSymbolBWT < char > transformed = BWTFromSuffixArrayNaive::construct ( sa );

                INFO( "transformed: " << transformed << "; reference: " << reference );
                REQUIRE ( compareStringContent ( transformed, reference ));
            }
        }

        SECTION ( "test on normalized strings (object::Object)" ) {
            auto originalNormalized = core::normalize < LinearString < char > >::eval ( std::move ( original ));
            auto referenceNormalized = core::normalize < LinearStringTerminatingSymbolBWT < char > >::eval (
                    std::move ( reference ));

            SECTION ( "BWTNaive::construct" ) {
                string::LinearStringTerminatingSymbolBWT < > transformed = BWTNaive::construct ( originalNormalized );

                INFO( "transformed: " << transformed << "; reference: " << referenceNormalized );
                REQUIRE ( compareStringContent ( transformed, referenceNormalized ));
            }

            SECTION ( "BWTFromSuffixArray::construct (via SuffixArrayNaive::construct)" ) {
                auto sa = stringology::indexing::SuffixArrayNaive::construct ( originalNormalized );
                string::LinearStringTerminatingSymbolBWT < > transformed = BWTFromSuffixArrayNaive::construct ( sa );

                INFO( "transformed: " << transformed << "; reference: " << referenceNormalized );
                REQUIRE ( compareStringContent ( transformed, referenceNormalized ));
            }
        }
    }

}