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

#pragma once

#include <indexes/stringology/SimpleFMIndex.h>

namespace stringology {

namespace common {

class FMIndexCommon {
public:
    /** Compute the number of occurrences of *symbol* in L[0..index] inclusive, where L is the BWT-encoded string.
     * In pseudocode: occ(symbol, index) */
    template < class SymbolType >
    static unsigned occ ( const indexes::stringology::SimpleFMIndex < SymbolType > & fmIndex, const SymbolType & symbol,
                          unsigned index );

    /** Get the sum of occurrences of all symbols less than c in the ordered alphabet of the string.
     * In pseudocode: m[c] */
    template < class SymbolType >
    static unsigned m ( const indexes::stringology::SimpleFMIndex < SymbolType > & fmIndex, const SymbolType & c );

    /** Get the sum of occurrences of all symbols less or equal to c in the ordered alphabet of the string.
     * In pseudocode: m[c+1] **/
    template < class SymbolType >
    static unsigned mNext ( const indexes::stringology::SimpleFMIndex < SymbolType > & fmIndex, const SymbolType & c );

    /** Check whether the row (symbol) at i is marked (has known location data). */
    template < class SymbolType >
    static bool isMarked ( const indexes::stringology::SimpleFMIndex < SymbolType > & index, unsigned i );

    /** Get location of i in the decoded text. The row must be marked (isMarked(index, i) must return true). */
    template < class SymbolType >
    static unsigned pos ( const indexes::stringology::SimpleFMIndex < SymbolType > & index, unsigned i );
};

template < class SymbolType >
unsigned
FMIndexCommon::occ ( const indexes::stringology::SimpleFMIndex < SymbolType > & fmIndex, const SymbolType & symbol,
                     unsigned int index ) {
    unsigned superBucketIndex = index / fmIndex.getSuperBucketSize ( );
    unsigned bucketIndex = index / fmIndex.getBucketSize ( );
    unsigned relativeBucketIndex =
            bucketIndex - superBucketIndex * fmIndex.getSuperBucketSize ( ) / fmIndex.getBucketSize ( );
    unsigned relativeIndex = index - bucketIndex * fmIndex.getBucketSize ( );

    unsigned res = 0;
    if ( superBucketIndex > 0 ) {
        // sum occurrences in previous superbuckets
        const ext::map < SymbolType, unsigned > & sb = fmIndex.getSuperBucketOcc ( )[ superBucketIndex ];
        auto it = sb.find ( symbol );
        if ( it != sb.end ( ))
            res += it->second;
    }
    if ( relativeBucketIndex > 0 ) {
        // sum occurrences in previous buckets in the current superbucket
        const ext::map < SymbolType, unsigned > & b = fmIndex.getBucketOcc ( )[ bucketIndex ];
        auto it = b.find ( symbol );
        if ( it != b.end ( ))
            res += it->second;
    }
    // sum occurrences in current bucket (symbol at index is included)
    for ( unsigned i = index - relativeIndex; i <= index; i ++ ) {
        const SymbolType & s = fmIndex.getString ( ).getContent ( )[ i ];
        if ( s == symbol )
            res ++;
    }

    return res;
}

template < class SymbolType >
unsigned
FMIndexCommon::m ( const indexes::stringology::SimpleFMIndex < SymbolType > & fmIndex, const SymbolType & c ) {
    return fmIndex.getMOccPrefixSum ( ).at ( c );
}

template < class SymbolType >
unsigned
FMIndexCommon::mNext ( const indexes::stringology::SimpleFMIndex < SymbolType > & fmIndex, const SymbolType & c ) {
    return fmIndex.getMOccPrefixSum ( ).at ( c ) +
           fmIndex.getKOcc ( ).at ( c ); // from definition: M[c+1] = M[c] + K[c]
}

template < class SymbolType >
bool FMIndexCommon::isMarked ( const indexes::stringology::SimpleFMIndex < SymbolType > & index, unsigned int i ) {
    return index.getString ( ).getContent ( )[ i ] == index.getMarkedSymbol ( );
}

template < class SymbolType >
unsigned int FMIndexCommon::pos ( const indexes::stringology::SimpleFMIndex < SymbolType > & index, unsigned int i ) {
    return index.getSparseFirstToText ( )[ occ ( index, index.getMarkedSymbol ( ), i ) - 1 ];
}

} /* namespace common */

} /* namespace stringology */