/*******************************************************************************
;*******************************************************************************
;**                                                                           **
;**                  COPYRIGHT 2001-2012 NUANCE COMMUNICATIONS                **
;**                                                                           **
;**               NUANCE COMMUNICATIONS PROPRIETARY INFORMATION               **
;**                                                                           **
;**     This software is supplied under the terms of a license agreement      **
;**     or non-disclosure agreement with Nuance Communications and may not    **
;**     be copied or disclosed except in accordance with the terms of that    **
;**     agreement.                                                            **
;**                                                                           **
;*******************************************************************************
;**                                                                           **
;**     FileName: et9aspc.h                                                   **
;**                                                                           **
;**  Description: Alphabetic spell correction routines.                       **
;**                                                                           **
;*******************************************************************************
;******************************************************************************/

/*! -IDR-                        
*   
*/

#include "et9api.h"
#ifdef ET9_ALPHABETIC_MODULE
#include "et9sym.h"
#include "et9asym.h"
#include "et9aspc.h"
#include "et9asys.h"
#include "et9misc.h"


#ifdef ET9_SPC_LOG_HELP_FUNC
#include <stdio.h>
FILE *pLogFile = NULL;
FILE *pLogFile2 = NULL;
#endif /* ET9_SPC_LOG_HELP_FUNC */


#ifdef ET9_ACTIVATE_SLST_STATS

#ifdef _WIN32
#pragma message ("*** SLST stats active ***")
#endif

ET9S64 ET9AWSPCCmpOther = 0;
ET9S64 ET9AWSPCCmpLdbOnly = 0;
ET9S64 ET9AWSPCCmpLdbInfo = 0;

ET9S64 ET9AWSPCCmpOtherAcc = 0;
ET9S64 ET9AWSPCCmpLdbOnlyAcc = 0;
ET9S64 ET9AWSPCCmpLdbInfoAcc = 0;

ET9S64 ET9AWSPCTotLdbCount = 0;
ET9S64 ET9AWSPCTotCalcCount = 0;
ET9S64 ET9AWSPCClassicMatch = 0;
ET9S64 ET9AWSPCNoStemMatch = 0;
ET9S64 ET9AWSPCPossibleSpcCandidate = 0;
ET9S64 ET9AWSPCWithinCandidate = 0;
ET9S64 ET9AWSPCEdCalcCandidate = 0;
ET9S64 ET9AWSPCActualCandidate = 0;

ET9S64 ET9AWSPCTotLdbCountAcc = 0;
ET9S64 ET9AWSPCTotCalcCountAcc = 0;
ET9S64 ET9AWSPCClassicMatchAcc = 0;
ET9S64 ET9AWSPCNoStemMatchAcc = 0;
ET9S64 ET9AWSPCPossibleSpcCandidateAcc = 0;
ET9S64 ET9AWSPCWithinCandidateAcc = 0;
ET9S64 ET9AWSPCEdCalcCandidateAcc = 0;
ET9S64 ET9AWSPCActualCandidateAcc = 0;

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                           
 *
 *                                                   
 *                                                                              
 *
 *             
 */

void _ET9AWSpcPrintStats(FILE *pf, ET9AWLingInfo *pLingInfo)
{
    ET9AWLingCmnInfo * const pLingCmnInfo = pLingInfo->pLingCmnInfo;

    fprintf(pf, "\nCompare stats: ldb only %I64d (%I64d), ldb info %I64d (%I64d), other %I64d (%I64d)\n\n",
        ET9AWSPCCmpLdbOnly,
        ET9AWSPCCmpLdbOnlyAcc,
        ET9AWSPCCmpLdbInfo,
        ET9AWSPCCmpLdbInfoAcc,
        ET9AWSPCCmpOther,
        ET9AWSPCCmpOtherAcc);

    if (ET9AWSPCTotCalcCount) {
        fprintf(pf, "SPC stats (one): tot count %I64d (%I64d), classic match %I64d, no stem match %I64d, possible spc %I64d, within %I64d, ed calc %I64d, actual %I64d\n\n",
            ET9AWSPCTotCalcCount,
            ET9AWSPCTotLdbCount,
            ET9AWSPCClassicMatch,
            ET9AWSPCNoStemMatch,
            ET9AWSPCPossibleSpcCandidate,
            ET9AWSPCWithinCandidate,
            ET9AWSPCEdCalcCandidate,
            ET9AWSPCActualCandidate);
    }

    fprintf(pf, "SPC stats (acc): tot count %I64d (%I64d), classic match %I64d, no stem match %I64d, possible spc %I64d, within %I64d, ed calc %I64d, actual %I64d\n\n",
        ET9AWSPCTotCalcCountAcc,
        ET9AWSPCTotLdbCountAcc,
        ET9AWSPCClassicMatchAcc,
        ET9AWSPCNoStemMatchAcc,
        ET9AWSPCPossibleSpcCandidateAcc,
        ET9AWSPCWithinCandidateAcc,
        ET9AWSPCEdCalcCandidateAcc,
        ET9AWSPCActualCandidateAcc);

    if (pLingCmnInfo->Private.sStats.dwTotInsertCount) {
        fprintf(pf, "List stats (one): builds %d, ins %d (%1.1f), max %d, duplicate %d (%1.1f%%), discard %d (%1.1f%%), replace %d (%1.1f%%)\n\n",
            pLingCmnInfo->Private.sStats.dwBuildCount,
            pLingCmnInfo->Private.sStats.dwTotInsertCount,
            (((float)pLingCmnInfo->Private.sStats.dwTotInsertCount) / ((float)pLingCmnInfo->Private.sStats.dwBuildCount)),
            pLingCmnInfo->Private.sStats.dwMaxListInserts,
            pLingCmnInfo->Private.sStats.dwTotInsertDuplicate,
            (((float)pLingCmnInfo->Private.sStats.dwTotInsertDuplicate) / ((float)pLingCmnInfo->Private.sStats.dwTotInsertCount) * 100),
            pLingCmnInfo->Private.sStats.dwTotInsertDiscarded,
            (((float)pLingCmnInfo->Private.sStats.dwTotInsertDiscarded) / ((float)pLingCmnInfo->Private.sStats.dwTotInsertCount) * 100),
            pLingCmnInfo->Private.sStats.dwTotInsertReplacing,
            (((float)pLingCmnInfo->Private.sStats.dwTotInsertReplacing) / ((float)pLingCmnInfo->Private.sStats.dwTotInsertCount)) * 100);
    }

    fprintf(pf, "List stats (acc): builds %d, ins %d (%1.1f), max %d, duplicate %d (%1.1f%%), discard %d (%1.1f%%), replace %d (%1.1f%%)\n\n",
        pLingCmnInfo->Private.sStats.dwBuildCountAcc,
        pLingCmnInfo->Private.sStats.dwTotInsertCountAcc,
        (((float)pLingCmnInfo->Private.sStats.dwTotInsertCountAcc) / ((float)pLingCmnInfo->Private.sStats.dwBuildCountAcc)),
        pLingCmnInfo->Private.sStats.dwMaxListInsertsAcc,
        pLingCmnInfo->Private.sStats.dwTotInsertDuplicateAcc,
        (((float)pLingCmnInfo->Private.sStats.dwTotInsertDuplicateAcc) / ((float)pLingCmnInfo->Private.sStats.dwTotInsertCountAcc) * 100),
        pLingCmnInfo->Private.sStats.dwTotInsertDiscardedAcc,
        (((float)pLingCmnInfo->Private.sStats.dwTotInsertDiscardedAcc) / ((float)pLingCmnInfo->Private.sStats.dwTotInsertCountAcc) * 100),
        pLingCmnInfo->Private.sStats.dwTotInsertReplacingAcc,
        (((float)pLingCmnInfo->Private.sStats.dwTotInsertReplacingAcc) / ((float)pLingCmnInfo->Private.sStats.dwTotInsertCountAcc)) * 100);
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                                                  
 *
 *                                                                              
 *
 *             
 */

void _ET9AWSpcClearStats(ET9AWLingInfo *pLingInfo)
{
    ET9AWLingCmnInfo * const pLingCmnInfo = pLingInfo->pLingCmnInfo;

    ET9AWSPCCmpOtherAcc     += ET9AWSPCCmpOther;
    ET9AWSPCCmpLdbOnlyAcc   += ET9AWSPCCmpLdbOnly;
    ET9AWSPCCmpLdbInfoAcc   += ET9AWSPCCmpLdbInfo;

    ET9AWSPCCmpOther = 0;
    ET9AWSPCCmpLdbInfo = 0;
    ET9AWSPCCmpLdbOnly = 0;

    ET9AWSPCTotLdbCountAcc          += ET9AWSPCTotLdbCount;
    ET9AWSPCTotCalcCountAcc         += ET9AWSPCTotCalcCount;
    ET9AWSPCClassicMatchAcc         += ET9AWSPCClassicMatch;
    ET9AWSPCNoStemMatchAcc          += ET9AWSPCNoStemMatch;
    ET9AWSPCPossibleSpcCandidateAcc += ET9AWSPCPossibleSpcCandidate;
    ET9AWSPCWithinCandidateAcc      += ET9AWSPCWithinCandidate;
    ET9AWSPCEdCalcCandidateAcc      += ET9AWSPCEdCalcCandidate;
    ET9AWSPCActualCandidateAcc      += ET9AWSPCActualCandidate;

    ET9AWSPCTotLdbCount = 0;
    ET9AWSPCTotCalcCount = 0;
    ET9AWSPCClassicMatch = 0;
    ET9AWSPCNoStemMatch = 0;
    ET9AWSPCPossibleSpcCandidate = 0;
    ET9AWSPCWithinCandidate = 0;
    ET9AWSPCEdCalcCandidate = 0;
    ET9AWSPCActualCandidate = 0;

    pLingCmnInfo->Private.sStats.dwBuildCountAcc            += pLingCmnInfo->Private.sStats.dwBuildCount;
    pLingCmnInfo->Private.sStats.dwInsertCountAcc           += pLingCmnInfo->Private.sStats.dwInsertCount;
    pLingCmnInfo->Private.sStats.dwTotInsertCountAcc        += pLingCmnInfo->Private.sStats.dwTotInsertCount;
    pLingCmnInfo->Private.sStats.dwTotInsertDiscardedAcc    += pLingCmnInfo->Private.sStats.dwTotInsertDiscarded;
    pLingCmnInfo->Private.sStats.dwTotInsertReplacingAcc    += pLingCmnInfo->Private.sStats.dwTotInsertReplacing;
    pLingCmnInfo->Private.sStats.dwTotInsertDuplicateAcc    += pLingCmnInfo->Private.sStats.dwTotInsertDuplicate;

    if (pLingCmnInfo->Private.sStats.dwMaxListInsertsAcc < pLingCmnInfo->Private.sStats.dwMaxListInserts) {
        pLingCmnInfo->Private.sStats.dwMaxListInsertsAcc = pLingCmnInfo->Private.sStats.dwMaxListInserts;
    }

    pLingCmnInfo->Private.sStats.dwBuildCount = 0;
    pLingCmnInfo->Private.sStats.dwInsertCount = 0;
    pLingCmnInfo->Private.sStats.dwTotInsertCount = 0;
    pLingCmnInfo->Private.sStats.dwTotInsertDiscarded = 0;
    pLingCmnInfo->Private.sStats.dwTotInsertReplacing = 0;
    pLingCmnInfo->Private.sStats.dwTotInsertDuplicate = 0;
    pLingCmnInfo->Private.sStats.dwMaxListInserts = 0;
}

#endif /* ET9_ACTIVATE_SLST_STATS */

#ifdef ET9_DEBUG_FLEXVER

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                        
 *
 *                                                        
 *                                                          
 *
 *                           
 */

static ET9U32 ET9LOCALCALL __CalcChecksum(void * const pMemory, const ET9U32 dwSize)
{
    ET9U8               *pbByte;
    ET9U32              dwCount;
    ET9U32              dwHashValue;

    ET9Assert(pMemory);

    dwHashValue = 0;

    pbByte = (ET9U8*)pMemory;
    for (dwCount = dwSize; dwCount; --dwCount, ++pbByte) {
        dwHashValue = *pbByte + (65599 * dwHashValue);
    }

    return dwHashValue;
}

#endif

#ifdef ET9_DEBUG_FLEXVER

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                               
 *
 *                                                                                  
 *
 *             
 */

void ET9FARCALL _ET9AWValidateFlexArea(ET9AWLingCmnInfo * const pLingCmnInfo)
{
    ET9ASPCFlexCompareData  * const pCMP = &pLingCmnInfo->Private.ASpc.u.sCmpDataFlex;

    ET9Assert(pLingCmnInfo->Private.ASpc.dwCompareChecksum == __CalcChecksum(pCMP, sizeof(ET9ASPCFlexCompareData)));
}

#endif

#ifdef ET9_DEBUG_FLEXVER

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                               
 *
 *                                                                                  
 *
 *             
 */

void ET9FARCALL _ET9AWModifiedFlexArea(ET9AWLingCmnInfo * const pLingCmnInfo)
{
    ET9ASPCFlexCompareData  * const pCMP = &pLingCmnInfo->Private.ASpc.u.sCmpDataFlex;

    pLingCmnInfo->Private.ASpc.dwCompareChecksum = __CalcChecksum(pCMP, sizeof(ET9ASPCFlexCompareData));
}

#endif

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                                      
 *
 *                                                                                  
 *
 *                                   
 */

ET9ASPCSEARCHFILTER ET9FARCALL _ET9AWGetSpcSearchFilter(ET9AWLingCmnInfo const * const pLingCmnInfo)
{
    if (pLingCmnInfo->Private.bStateNiceLatency) {

        if (pLingCmnInfo->Private.ASpc.eSearchFilter != ET9ASPCSEARCHFILTER_ONE_EXACT) {

            return ET9ASPCSEARCHFILTER_TWO_EXACT;
        }
    }

    return pLingCmnInfo->Private.ASpc.eSearchFilter;
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                                            
 *
 *                                                                                  
 *                                                        
 *                                                         
 *
 *                           
 */

ET9INLINE static ET9UINT ET9LOCALCALL __CalcMaxEditDistance(ET9AWLingCmnInfo const * const pLingCmnInfo, const ET9BOOL bSpcAllowed, const ET9UINT nLength)
{
    if (!bSpcAllowed) {
        return 0;
    }

    {
        const ET9UINT nMaxDistance = (pLingCmnInfo->Private.bStateNiceLatency) ? 2 : ET9_SPC_ED_MAX_DISTANCE;

        return (nLength >= 3 * nMaxDistance) ? nMaxDistance : (nLength / 3);
    }
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                                                                            
 *
 *                                                                  
 *                                                         
 *                                                        
 *
 *                             
 */

ET9INLINE static ET9UINT ET9LOCALCALL __CalcMinSourceLength(const ET9BOOL bComponent, const ET9UINT nLength, const ET9U8 nMaxDist)
{
    if (bComponent || (nLength <= nMaxDist)) {
        return 1;
    }
    else {
        return nLength - nMaxDist;
    }
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                                                                            
 *
 *                                                                         
 *                                                                  
 *                                                         
 *                                                        
 *
 *                             
 */

ET9INLINE static ET9UINT ET9LOCALCALL __CalcMaxSourceLength(const ET9U8 bSpcFeatures, const ET9BOOL bComponent, const ET9UINT nLength, const ET9U8 nMaxDist)
{
    if (!bComponent && ET9_FLEX_FEATURE_SPC_COMPL_MODE(bSpcFeatures)) {
        return ET9MAXWORDSIZE;
    }

    return nLength + nMaxDist;
}

#ifdef _ET9_ACTIVATE_ADDB_COMPARE_BIT_HASH

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                           
 *
 *                                                                              
 *
 *             
 */

static void ET9LOCALCALL __SetUpDynamicMatchingInfo(ET9AWLingInfo     * const pLingInfo)
{
    ET9AWLingCmnInfo * const pLingCmnInfo = pLingInfo->pLingCmnInfo;
    ET9WordSymbInfo * const pWordSymbInfo = pLingCmnInfo->Base.pWordSymbInfo;

    _ET9ClearMem((ET9U8*)pLingCmnInfo->Private.ASpc.ddbHashInfo, sizeof(pLingCmnInfo->Private.ASpc.ddbHashInfo));

    if (pWordSymbInfo->bNumSymbs) {

        ET9UINT nSymbIndex;

        for (nSymbIndex = 0; nSymbIndex < pWordSymbInfo->bNumSymbs; ++nSymbIndex) {

            ET9SymbInfo const * const pSymbInfo = &pWordSymbInfo->SymbsInfo[nSymbIndex];

            const ET9BOOL bSymbLocked = pSymbInfo->bCurrBuildLocked && !pLingCmnInfo->Private.sWordC.pCurrC->bPureCollection;

            ET9UINT nBaseIndex;

            pLingCmnInfo->Private.ASpc.pbLockInfo[nSymbIndex] = bSymbLocked ? ET9_LOCKED_SYMB_VALUE_AND_POS : ET9_SPC_IS_LOCKED_POS(pSymbInfo);

            if (bSymbLocked) {

                {
                    const ET9UINT nCode = pSymbInfo->sLockedSymb & 0xFF;  /* assuming 256 values as per below config check */

                    __ET9BitSet(pLingCmnInfo->Private.ASpc.ddbHashInfo[nSymbIndex], nCode);
                }

                {
                    const ET9UINT nCode = _ET9SymToOther(pSymbInfo->sLockedSymb, pLingCmnInfo->dwLdbNum) & 0xFF;  /* assuming 256 values as per below config check */

                    __ET9BitSet(pLingCmnInfo->Private.ASpc.ddbHashInfo[nSymbIndex], nCode);
                }
            }
            else {

                for (nBaseIndex = 0; nBaseIndex < pSymbInfo->bNumBaseSyms; ++nBaseIndex) {

                    ET9DataPerBaseSym const * const pBaseInfo = &pSymbInfo->DataPerBaseSym[nBaseIndex];

                    ET9UINT nCharIndex;

                    for (nCharIndex = 0; nCharIndex < pBaseInfo->bNumSymsToMatch; ++nCharIndex) {

                        {
                            const ET9UINT nCode = pBaseInfo->sChar[nCharIndex] & 0xFF;  /* assuming 256 values as per below config check */

                            __ET9BitSet(pLingCmnInfo->Private.ASpc.ddbHashInfo[nSymbIndex], nCode);
                        }

                        {
                            const ET9UINT nCode = pBaseInfo->sUpperCaseChar[nCharIndex] & 0xFF;  /* assuming 256 values as per below config check */

                            __ET9BitSet(pLingCmnInfo->Private.ASpc.ddbHashInfo[nSymbIndex], nCode);
                        }
                    }
                }
            }
        }
    }
}

#if ADDB_COMPARE_MAX_HASH_BYTES * 8 != 256
#error Bad config for ADDB_COMPARE_MAX_HASH_BYTES
#endif

#endif /* _ET9_ACTIVATE_ADDB_COMPARE_BIT_HASH */

#ifdef _ET9_ACTIVATE_ADDB_COMPARE_CHAR_HASH

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                
 *
 *                                                       
 *                                                   
 *                                                             
 *                                                                       
 *
 *             
 */

ET9INLINE static void ET9LOCALCALL __AddCharToHashTable(ET9ADdbCharHashItem * const pHashTable,
                                                        const ET9SYMB               sChar,
                                                        const ET9UINT               nBaseIndex,
                                                        const ET9BOOL               bExact)
{
    ET9UINT nIndex;
    ET9UINT nSentinel;

    ET9Assert(sChar);

    nIndex = (sChar & 0xFF) << 1;

    for (nSentinel = ADDB_COMPARE_MAX_CHAR_HASH_SIZE; nSentinel; --nSentinel) {

        ET9Assert(nIndex < ADDB_COMPARE_MAX_CHAR_HASH_SIZE);

        {
            const ET9SYMB sItemChar = pHashTable->psHashTable[nIndex];

            if (sItemChar == sChar) {

                ET9Assert((ET9UINT)(pHashTable->pbSymbIndex[nIndex] & 0x7F) <= nBaseIndex);
                return;
            }
            else if (!sItemChar) {

                pHashTable->psHashTable[nIndex] = sChar;
                pHashTable->pbSymbIndex[nIndex] = (ET9U8)(nBaseIndex | (bExact ? 0x80 : 0));
                return;
            }

            ++nIndex;

            if (nIndex >= ADDB_COMPARE_MAX_CHAR_HASH_SIZE) {
                nIndex = 0;
            }
        }
    }

    ET9Assert(0);
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                
 *
 *                                                                              
 *                                             /                               
 *                                                                    
 *                                                                        
 *
 *             
 */

static void ET9LOCALCALL __SetUpDynamicCharMatchingInfo(ET9AWLingInfo     * const pLingInfo,
                                                        const ET9U16              wIndex,
                                                        const ET9U16              wLength,
                                                        const ET9U8               bLimitedLock)
{
    ET9AWLingCmnInfo * const pLingCmnInfo = pLingInfo->pLingCmnInfo;
    ET9WordSymbInfo * const pWordSymbInfo = pLingCmnInfo->Base.pWordSymbInfo;

    ET9Assert(wIndex + wLength <= pWordSymbInfo->bNumSymbs);

    _ET9ClearMem((ET9U8*)pLingCmnInfo->Private.ASpc.ddbCharHashInfo, sizeof(pLingCmnInfo->Private.ASpc.ddbCharHashInfo));

    if (pWordSymbInfo->bNumSymbs) {

        const ET9UINT nEndIndex = wIndex + wLength;

        ET9UINT nSymbIndex;

        for (nSymbIndex = wIndex; nSymbIndex < nEndIndex; ++nSymbIndex) {

            ET9SymbInfo const * const pSymbInfo = &pWordSymbInfo->SymbsInfo[nSymbIndex];

            const ET9BOOL bSymbLocked = pSymbInfo->bCurrBuildLocked && !pLingCmnInfo->Private.sWordC.pCurrC->bPureCollection;

            pLingCmnInfo->Private.ASpc.pbLockInfo[nSymbIndex] = bSymbLocked ? ET9_LOCKED_SYMB_VALUE_AND_POS : (bLimitedLock) ? 0 : ET9_SPC_IS_LOCKED_POS(pSymbInfo);

            if (nSymbIndex >= ADDB_COMPARE_MAX_CHAR_HASH_POS) {
                continue;
            }
            else {

                ET9ADdbCharHashItem * const pHashTable = &pLingCmnInfo->Private.ASpc.ddbCharHashInfo[nSymbIndex];

                if (bSymbLocked) {

                    __AddCharToHashTable(pHashTable, pSymbInfo->sLockedSymb, 0, 1);
                    __AddCharToHashTable(pHashTable, _ET9SymToOther(pSymbInfo->sLockedSymb, pLingCmnInfo->dwLdbNum), 0, 1);
                }
                else {

                    ET9UINT nBaseIndex;

                    for (nBaseIndex = 0; nBaseIndex < pSymbInfo->bNumBaseSyms; ++nBaseIndex) {

                        ET9DataPerBaseSym const * const pBaseInfo = &pSymbInfo->DataPerBaseSym[nBaseIndex];

                        const ET9BOOL bLimited = pBaseInfo->bLimited;

                        ET9U8 bSymFreq;
                        ET9BOOL bExact;

                        if (bLimited && pLingCmnInfo->Private.bCurrLimitedRegionality) {
                            continue;
                        }

                        if (bLimited &&
                            pSymbInfo->bCurrBuildAfterFlush &&
                            _ET9_IsPunctOrNumeric(pSymbInfo->DataPerBaseSym[0].sChar[0])) {

                            /* disallow limited symbols for punct/numeric buildaround points after a flush */

                            continue;
                        }

                        if (pSymbInfo->eInputType == ET9DISCRETEKEY ||
                            pSymbInfo->eInputType == ET9MULTITAPKEY ||
                            pSymbInfo->eInputType == ET9CUSTOMSET ||
                            pSymbInfo->bSymbType == ET9KTSMARTPUNCT) {

                            bExact = 1;
                        }
                        else {

                            bExact = (ET9BOOL)!nBaseIndex;

                            if (nBaseIndex >= pLingCmnInfo->Private.bCurrMaxRegionality) {
                                break;
                            }
                        }

                        bSymFreq = pBaseInfo->bSymFreq;

                        if (!bSymFreq) {
                            bSymFreq = 1;
                        }

                        {
                            ET9UINT nCharIndex;

                            for (nCharIndex = 0; nCharIndex < pBaseInfo->bNumSymsToMatch; ++nCharIndex) {

                                __AddCharToHashTable(pHashTable, pBaseInfo->sChar[nCharIndex], nBaseIndex, bExact);
                                __AddCharToHashTable(pHashTable, pBaseInfo->sUpperCaseChar[nCharIndex], nBaseIndex, bExact);
                            }
                        }
                    }
                }
            }
        }
    }
}

#endif /* _ET9_ACTIVATE_ADDB_COMPARE_CHAR_HASH */

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                                              
 *
 *                                                                              
 *                                             /                               
 *                                                                    
 *                                                        
 *                                                                        
 *
 *                                                                    
 */

ET9STATUS ET9FARCALL _ET9AWCalcEditDistanceInit(ET9AWLingInfo     * const pLingInfo,
                                                const ET9U16              wIndex,
                                                const ET9U16              wLength,
                                                const ET9U8               bSpcMode,
                                                const ET9U8               bSpcFeatures)
{
    ET9AWLingCmnInfo        * const pLingCmnInfo    = pLingInfo->pLingCmnInfo;
    ET9WordSymbInfo         * const pWordSymbInfo   = pLingCmnInfo->Base.pWordSymbInfo;
    ET9SymbInfo             * const pFirstSymb      = &pWordSymbInfo->SymbsInfo[wIndex];
    ET9ASPCFlexCompareData  * const pCMP            = &pLingCmnInfo->Private.ASpc.u.sCmpDataFlex;

    ET9U8                   * const pbLockInfo      = pCMP->pbLockInfo;
    ET9U8                   * const pbIsFreqPos     = pCMP->pbIsFreqPos;
    ET9U8                   * const pbIsQualityKey  = pCMP->pbIsQualityKey;

    const ET9UINT nKeyCount = wLength;
    const ET9UINT nSymbCount = ET9MAXWORDSIZE;

    ET9UINT nR;
    ET9UINT nC;

    ET9UINT nQualityCount;
    ET9UINT nTraceKeyCount;

    /* validate */

    ET9Assert(pLingCmnInfo->Private.ASpc.bSpcState == ET9_SPC_STATE_NONE);

    /* pre-calculate some common values */

    /* global */

    _ET9_PrecalculateSettings(pLingInfo, (ET9U8)(wLength ? (wIndex + wLength - 1) : 0xFF));

    /* limited regionality */

    pLingCmnInfo->Private.bCurrLimitedRegionality = (wLength > 1) ? 0 : 1;

    /* max regionality */

    pLingCmnInfo->Private.bCurrMaxRegionality = _ET9_LanguageSpecific_ApplyDynamicRegionality(pLingInfo, wLength);

    /* dynamic matching */

#ifdef _ET9_ACTIVATE_ADDB_COMPARE_BIT_HASH
    __SetUpDynamicMatchingInfo(pLingInfo);
#endif /* _ET9_ACTIVATE_ADDB_COMPARE_BIT_HASH */

#ifdef _ET9_ACTIVATE_ADDB_COMPARE_CHAR_HASH
    __SetUpDynamicCharMatchingInfo(pLingInfo, wIndex, wLength, bSpcFeatures);
#endif /* _ET9_ACTIVATE_ADDB_COMPARE_CHAR_HASH */

    /* just std init? */

    if (!bSpcFeatures) {

        const ET9U8 bCurrSpcMode = __CalcMaxEditDistance(pLingCmnInfo, 1, wLength) ? bSpcMode : ET9ASPCMODE_OFF;
        const ET9BOOL bUsingSpc = bCurrSpcMode != ET9ASPCMODE_OFF;

        pCMP->nFirstOffset = wIndex;
        pCMP->pFirstSymb = pFirstSymb;
        pCMP->nQualityCount = nKeyCount;

        pLingCmnInfo->Private.bCurrSpcMode = bCurrSpcMode;
        pLingCmnInfo->Private.bCurrMaxEditDistance = (ET9U8)__CalcMaxEditDistance(pLingCmnInfo, bUsingSpc, wLength);
        pLingCmnInfo->Private.wCurrMinSourceLength = (ET9U16)__CalcMinSourceLength(pLingCmnInfo->Private.ALdbMGD.bSupported, wLength, pLingCmnInfo->Private.bCurrMaxEditDistance);
        pLingCmnInfo->Private.wCurrMaxSourceLength = (ET9U16)__CalcMaxSourceLength(bSpcFeatures, pLingCmnInfo->Private.ALdbMGD.bSupported, wLength, pLingCmnInfo->Private.bCurrMaxEditDistance);

        pLingCmnInfo->Private.ASpc.bSpcState = ET9_SPC_STATE_STD_INIT_OK;

        return ET9STATUS_NONE;
    }

    pLingCmnInfo->Private.ASpc.bSpcState = ET9_SPC_STATE_FLEX_INIT_OK;

    /* locked symbs */

    {
        ET9SYMB * const psLockSymb      = pCMP->psLockSymb;
        ET9SYMB * const psLockSymbOther = pCMP->psLockSymbOther;

        for (nR = nKeyCount; nR; --nR) {

            const ET9UINT nIndex = nR - 1;

            if (pFirstSymb[nIndex].bCurrBuildLocked && !pLingCmnInfo->Private.sWordC.pCurrC->bPureCollection) {

                psLockSymb[nIndex] = pFirstSymb[nIndex].sLockedSymb;
                psLockSymbOther[nIndex] = _ET9SymToOther(psLockSymb[nIndex], pLingCmnInfo->Private.dwCurrActiveLanguage);
            }
            else {

                psLockSymb[nIndex] = 0;
                psLockSymbOther[nIndex] = 0;
            }
        }
    }

    /* initial quality count & trace count */

    nQualityCount = 0;
    nTraceKeyCount = 0;

    for (nR = 1; nR <= nKeyCount; ++nR) {

        const ET9U8 bQuality = _ET9_Flex_KeyQuality(pFirstSymb, nR - 1);
        const ET9BOOL bTraceKey = _ET9_Flex_TraceKey(pFirstSymb, nR - 1);

        if (bQuality) {
            ++nQualityCount;
        }

        if (bTraceKey) {
            ++nTraceKeyCount;
        }
    }

    {
        const ET9U8 bCurrSpcMode = __CalcMaxEditDistance(pLingCmnInfo, 1, nQualityCount) ? bSpcMode : ET9ASPCMODE_OFF;
        const ET9BOOL bUsingSpc = bCurrSpcMode != ET9ASPCMODE_OFF;

        pLingCmnInfo->Private.bCurrSpcMode = bCurrSpcMode;
        pLingCmnInfo->Private.bCurrMaxEditDistance = (ET9U8)__CalcMaxEditDistance(pLingCmnInfo, bUsingSpc, nQualityCount);
        pLingCmnInfo->Private.wCurrMinSourceLength = (ET9U16)__CalcMinSourceLength(pLingCmnInfo->Private.ALdbMGD.bSupported, nQualityCount, pLingCmnInfo->Private.bCurrMaxEditDistance);
        pLingCmnInfo->Private.wCurrMaxSourceLength = (ET9U16)__CalcMaxSourceLength(bSpcFeatures, pLingCmnInfo->Private.ALdbMGD.bSupported, nQualityCount, pLingCmnInfo->Private.bCurrMaxEditDistance);

        /* init top */

        pCMP->ppbFreeDist[0][0] = 0;
        pCMP->ppbEditDist[0][0] = 0;
        pCMP->ppbStemDist[0][0] = 0;
        pCMP->ppxStemFreq[0][0] = 1;
        pCMP->ppxSymbDist[0][0] = 0;

        pCMP->ppbEditDistSbt[0][0] = 0;
        pCMP->ppbEditDistTrp[0][0] = 0;
        pCMP->ppbEditDistIns[0][0] = 0;
        pCMP->ppbEditDistDel[0][0] = 0;

        pCMP->ppbLimitedCount[0][0] = 0;

        pCMP->pbComplLenFC[0] = 0;

        /* init rows */

        nQualityCount = 0;  /* re-count */

        for (nR = 1; nR <= nKeyCount; ++nR) {

            const ET9U8 bQuality = _ET9_Flex_KeyQuality(pFirstSymb, nR - 1);

            if (bQuality) {
                ++nQualityCount;
            }

            pbIsQualityKey[nR] = bQuality;

            pbLockInfo[nR] = _ET9_Flex_LockInfo(pFirstSymb, pCMP->psLockSymb, nR - 1);

            pbIsFreqPos[nR] = (bQuality && nQualityCount <= ET9_SPC_ED_MAX_FREQ_LEN) ? 1 : 0;

            pCMP->pbSubstFreqSC[nR][0] = 0;

            pCMP->ppbEditDist[nR][0] = pCMP->ppbEditDist[nR - 1][0] + (bQuality ? 1 : 0);

            if (bUsingSpc) {
                pCMP->ppbStemDist[nR][0] = pCMP->ppbStemDist[nR - 1][0] + (bQuality ? 1 : 0);
            }
            else {
                pCMP->ppbStemDist[nR][0] = (nQualityCount ? _ET9_FLEX_TUBE_MAX_STEM_DIST : 0);
            }

            pCMP->ppbFreeDist[nR][0] = pCMP->ppbEditDist[nR][0];
            pCMP->ppxStemFreq[nR][0] = 1;
            pCMP->ppxSymbDist[nR][0] = 0;

            pCMP->ppbEditDistSbt[nR][0] = 0;
            pCMP->ppbEditDistTrp[nR][0] = 0;
            pCMP->ppbEditDistIns[nR][0] = 0;
            pCMP->ppbEditDistDel[nR][0] = pCMP->ppbEditDistDel[nR - 1][0] + (bQuality ? 1 : 0);

            pCMP->ppbLimitedCount[nR][0] = 0;
        }

        /* init columns */

        for (nC = 1; nC <= nSymbCount; ++nC) {

            pCMP->pwPrevWordSC[nC] = ALDB_CHAR_CODE_NONE;
            pCMP->pwPrevWordFC[nC] = ALDB_CHAR_CODE_NONE;

            pCMP->ppbEditDist[0][nC] = _ET9_FLEX_TUBE_MAX_EDIT_DIST;
            pCMP->ppbStemDist[0][nC] = _ET9_FLEX_TUBE_MAX_STEM_DIST;
            pCMP->ppbFreeDist[0][nC] = _ET9_FLEX_TUBE_MAX_FREE_DIST;
            pCMP->ppxStemFreq[0][nC] = 1;
            pCMP->ppxSymbDist[0][nC] = 0;

            pCMP->ppbEditDistSbt[0][nC] = 0;
            pCMP->ppbEditDistTrp[0][nC] = 0;
            pCMP->ppbEditDistIns[0][nC] = _ET9_FLEX_TUBE_MAX_EDIT_DIST;
            pCMP->ppbEditDistDel[0][nC] = 0;

            pCMP->ppbLimitedCount[0][nC] = 0;
        }

        /* allow's */

        pCMP->bAllowSpcCmpl    = (ET9BOOL)(ET9_FLEX_FEATURE_SPC_COMPL_MODE(bSpcFeatures) ? 1 : 0);       /* Options enabled completion. */
        pCMP->bAllowFreePunct  = (ET9BOOL)(ET9_FLEX_FEATURE_FREE_PUNCT_MODE(bSpcFeatures) ? 1 : 0);
        pCMP->bAllowFreeDouble = (ET9BOOL)(ET9_FLEX_FEATURE_FREE_DOUBLE_MODE(bSpcFeatures) ? 1 : 0);

        /* persist */

        pCMP->nFirstOffset = wIndex;
        pCMP->pFirstSymb = pFirstSymb;
        pCMP->nQualityCount = nQualityCount;
        pCMP->nTraceKeyCount = nTraceKeyCount;
        pCMP->fTapFreqDivider = _ET9pow_f(10, (ET9FLOAT)__ET9Min(nQualityCount, ET9_SPC_ED_MAX_FREQ_LEN));
    }

    _ET9AWModifiedFlexArea(pLingCmnInfo);

    /* done */

    return ET9STATUS_NONE;
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                                                        
 *
 *                                                                              
 *
 *             
 */

void ET9FARCALL _ET9AWCalcEditDistanceDone(ET9AWLingInfo     * const pLingInfo)
{
    ET9AWLingCmnInfo        * const pLingCmnInfo    = pLingInfo->pLingCmnInfo;
    ET9ASPCFlexCompareData  * const pCMP            = &pLingCmnInfo->Private.ASpc.u.sCmpDataFlex;

    ET9Assert(pLingCmnInfo->Private.ASpc.bSpcState == ET9_SPC_STATE_STD_INIT_OK ||
              pLingCmnInfo->Private.ASpc.bSpcState == ET9_SPC_STATE_FLEX_INIT_OK);

    if (pLingCmnInfo->Private.ASpc.bSpcState == ET9_SPC_STATE_FLEX_INIT_OK) {
        _ET9AWValidateFlexArea(pLingCmnInfo);
    }

    pLingCmnInfo->Private.ASpc.bSpcState = ET9_SPC_STATE_NONE;

    pCMP->pFirstSymb = NULL;
    pCMP->nQualityCount = 0;
}


#endif /* ET9_ALPHABETIC_MODULE */
/*! @} */
/* ----------------------------------< eof >--------------------------------- */
