/*
 * FMIndexFactors.h
 *
 *  Created on: 27. 02. 2021
 *      Author: Martin Prokopic
 */

#pragma once

#include <indexes/stringology/SimpleFMIndex.h>
#include "FMIndexCount.h"

namespace stringology {

namespace query {

class FMIndexFactors {
    /**
     * Get the location in the decoded text, which is represented by the first symbol of row i in the sorted cyclic
     * shift table. Same as hrFirstToText[i], but without the Hr array.
     *
     * @tparam SymbolType
     * @param fmIndex the input FM Index
     * @param i the row index
     * @return the corresponding index in the decoded text
     */
    template < class FMIndex >
    static unsigned locate ( const FMIndex & fmIndex, unsigned i );

public:
    /**
     * Use Simple FM Index to find occurrences of pattern in BWT-encoded subject.
     *
     * @tparam SymbolType
     * @param fmIndex the input FM Index
     * @param pattern the pattern to search for
     * @return indices of occurrences of pattern in the original (decoded) text, starting at 0
     */
    template < class FMIndex, class SymbolType >
    static ext::set < unsigned > match ( const FMIndex & fmIndex,
                                         const string::LinearString < SymbolType > & pattern );
};

template < class FMIndex, class SymbolType >
ext::set < unsigned > FMIndexFactors::match ( const FMIndex & fmIndex,
                                              const string::LinearString < SymbolType > & pattern ) {
    if ( fmIndex.getSparseFirstToText ( ).empty ( ))
        throw exception::CommonException (
                "Locate operation is only available on fat FM-indices (fttDensityPct > 0)." );

    measurements::start ( "FMIndexFactors", measurements::Type::ALGORITHM );

    auto[sp, ep] = stringology::query::FMIndexCount::getBounds ( fmIndex, pattern );

    if ( sp == ep )
        return ext::set < unsigned > ( );

    measurements::start ( "Locate", measurements::Type::ALGORITHM );
    ext::set < unsigned int > matches;
    for ( size_t j = sp; j < ep; j ++ )
        matches.insert ( locate ( fmIndex, j ));
    measurements::end ( );

    measurements::end ( );

    return matches;
}

template < class FMIndex >
unsigned FMIndexFactors::locate ( const FMIndex & fmIndex, unsigned int i ) {
    unsigned v = 0;
    while ( ! stringology::common::FMIndexCommon::isMarked ( fmIndex, i )) {
        const auto & c = fmIndex.getString ( ).getContent ( )[ i ];
        unsigned m = stringology::common::FMIndexCommon::occ ( fmIndex, c, i );
        i = ( stringology::common::FMIndexCommon::m ( fmIndex, c ) + m - 1 ) % fmIndex.getString ( ).size ( );
        v ++;
    }
    return ( stringology::common::FMIndexCommon::pos ( fmIndex, i ) + v ) % fmIndex.getString ( ).size ( );
}

} /* namespace query */

} /* namespace stringology */

