/*******************************************************************************
;*******************************************************************************
;**                                                                           **
;**                  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: et9aspath.cpp                                               **
;**                                                                           **
;**  Description: Alphabetic Swype path module.                               **
;**                                                                           **
;*******************************************************************************
;******************************************************************************/


/*! \addtogroup et9slst Selection list for alphabetic
* Selection list for alphabetic XT9.
* @{
*/

#include "et9api.h"
#ifdef ET9_SPM_MODULE
#include "et9misc.h"
#include "et9aspath.h"
#include "et9sym.h"
#include "et9asym.h"
#include "et9aslst.h"

#include "et9aoembuild.h"

#if defined(ET9_DEBUG) && LOGGING
#include <stdarg.h>
#ifdef _MSC_VER
#include <windows.h>
#endif
#endif


#if !defined(_DEBUG) && !defined(NDEBUG)
#define NDEBUG
#endif

#include "et9abackend.h"

#define __USE_SWYPE           1
#define __USE_SWYPE_FREQ      0

#define __MIN_KEYBOARD_ROWS     3
#define __MAX_KEYBOARD_ROWS     5

#ifdef ET9_KDB_SWYPE_TEST_MODULE

ET9SYMB gsExpectedWord[ET9MAXWORDSIZE];
ET9BOOL gbExpectedWordFound;

#endif

#define __SWYPE_MEM_ALIGN     4   /* Keep memory chunks 4-byte aligned. This is
                                     certainly necessary in WinMobile, not sure
                                     about other platforms. */

#define __SWYPE_MEM_MARKER    0xA /* Recognizable value, 1010 in binary. */

typedef struct __SwypeMemBlock_s {  /* Header placed on each memory block. Note this is setup to */
                                    /* have a size that matches the memory alignment above. */
    ET9U16  size;                   /* Size of the memory block. */
    ET9U8   marker;                 /* Recognizable value for testing memory manager. */
    ET9U8   free;                   /* Boolean value indicating free or used. */
}
__SwypeMemBlock;

#include "et9alogutils.h"
#include "et9autilities.h"

#if defined(ET9_DEBUG) && LOGGING
/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                          
 *
 *                                                           
 *                                                            
 *                                                                           
 *        
 */

ET9PRIVATE const ET9U32 gLevel = SWLogger_INFO;

void ET9FARCALL __ET9AWSP_log(ET9U32 level, const ET9SYMB* format, ...)
{
#if DEBUG_OUTPUT_TO_FILE
    static FILE *gpSwypeLog = NULL;
    if (NULL == gpSwypeLog)
    {
        gpSwypeLog = fopen("SwypeLog.txt", "w");
    }
#endif /* DEBUG_OUTPUT_TO_FILE */

    /* static BYTE4 gLevel = SWLogger_DEBUG; */
    if (level <= gLevel)
    {
        va_list ap;
        va_start(ap, format);
#if DEBUG_OUTPUT_TO_FILE
        if (NULL != gpSwypeLog)
        {
            fwprintf(gpSwypeLog, format, ap);
            fflush(gpSwypeLog);
        }
#else
#ifdef _MSC_VER
        {
            wchar_t outstr[200];
            vswprintf_s(outstr, 200, (const wchar_t *)format, ap);
            OutputDebugString(outstr);
        }
#else
        (void)format;
#endif /* _MSC_VER */
#endif /* DEBUG_OUTPUT_TO_FILE */
        va_end(ap);
    }
}
#endif

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                                                            
 *                              
 *
 *                                                                            
 *                       
 *
 *                                                                                               
 *                                                                              
 *                                    
 */

void * ET9FARCALL  __ET9AWSP_malloc(_ET9AWSPContext *pContext, size_t size)
{
    void *pReturn = 0;

    /* Calculate the size of the memory block we need. */
    size_t align = (size % __SWYPE_MEM_ALIGN) ? __SWYPE_MEM_ALIGN - (size % __SWYPE_MEM_ALIGN) : 0;
    size_t block = size + sizeof(__SwypeMemBlock) + align;

    /* TODO: Search for a freed block that is big enough. */

    /* Check if we have enough memory left. */
    if ((pContext->pSpmMemoryFree + block) <= ((ET9U8*)pContext->pPreAllocMem + ET9AWSPMEMSIZE))
    {
        /* Give out the next chunk. */
        __SwypeMemBlock *pHeader = (__SwypeMemBlock*)pContext->pSpmMemoryFree;
        pHeader->marker = __SWYPE_MEM_MARKER;
        pHeader->free   = (ET9U8)0;
        pHeader->size   = (ET9U16)size;
        pReturn = (void*)(pContext->pSpmMemoryFree + sizeof(__SwypeMemBlock));

        /* Move the free pointer past the new chunk and keep it 4-byte aligned. */
        pContext->pSpmMemoryFree = pContext->pSpmMemoryFree + block;
        Log(SWLogger_INFO, SWTEXT("Allocated %d bytes, %d%% remaining. [%d, %d, 0x%x]\n"), size,
            100 - (int)((float)(pContext->pSpmMemoryFree - (ET9U8*)pContext->pPreAllocMem) / (float)ET9AWSPMEMSIZE * (float)100),
            align, block, pContext->pSpmMemoryFree);
    }
    else
    {
        Log(SWLogger_INFO, SWTEXT("Failed to allocate %d bytes.\n"), size);
        if (pContext->pSpmGlobals != 0) {
            __DATA->bLowMemory = _TRUE;
        }
    }
    ET9Assert(pReturn != 0);
    return pReturn;
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                                                        
 *                           
 *
 *                                                                        
 *               
 */

ET9BOOL ET9FARCALL __ET9AWSP_AssertionFailed(const char * assertion)
{
    ET9_UNUSED(assertion);
    Log(SWLogger_ERROR, SWTEXT("Assertion failed (%S)\n"), assertion);
    ET9Assert(_FALSE);
    return _FALSE;
}

/* Include all Swype code */
#include "et9asearchdb.c"
#include "et9ageometry.c"
#include "et9aipanalyzer.c"
#include "et9aiptable.c"
#include "et9afixeddata.c"
#include "et9aword.c"

#include "et9kbdef.h"


/* Swype maps all coordinates to a QVGA keyboard internally */
static const ET9U16         __wSwypeKeyboardWidthQVGA = QVGA_MAX_X;
static const ET9U16         __wSwypeKeyboardHeightQVGA[__SWYPE_MAX_ROWS + 1] = {
    QVGA_MAX_Y_3ROW,    /* 0 (indexed by natural row count) */
    QVGA_MAX_Y_3ROW,
    QVGA_MAX_Y_3ROW,
    QVGA_MAX_Y_3ROW,    /* 3 */
    QVGA_MAX_Y,
    QVGA_MAX_Y,         /* 5 */
    QVGA_MAX_Y,
    QVGA_MAX_Y,
    QVGA_MAX_Y          /* 8 */
};
static const ET9SYMB        __SymbGreekCapitalSigma = 931;
static const ET9SYMB        __SymbGreekSigma = 963;
static const ET9SYMB        __SymbGreekFinalSigma = 962;


ET9INLINE static ET9S16 __XIntCoordAsQVGA(_ET9AWSPContext *pContext, ET9S16 coord)
{
    return ((__DATA->wKeyboardScaleXNumerator * (coord - __DATA->wLayoutLeft)) + (__DATA->wKeyboardScaleXDenominator >> 1)) / __DATA->wKeyboardScaleXDenominator;
}

ET9INLINE static ET9S16 __YIntCoordAsQVGA(_ET9AWSPContext *pContext, ET9S16 coord)
{
    return ((__DATA->wKeyboardScaleYNumerator * (coord - __DATA->wLayoutTop)) + (__DATA->wKeyboardScaleYDenominator >> 1)) / __DATA->wKeyboardScaleYDenominator;
}

ET9INLINE static ET9S16 __XCoordAsQVGA(_ET9AWSPContext *pContext, ET9FLOAT coord)
{
    return (SBYTE2)(((coord - __DATA->wLayoutLeft) * __DATA->fKeyboardScaleX) + 0.5f);
}

ET9INLINE static ET9S16 __YCoordAsQVGA(_ET9AWSPContext *pContext, ET9FLOAT coord)
{
    return (SBYTE2)(((coord - __DATA->wLayoutTop) * __DATA->fKeyboardScaleY) + 0.5f);
}

ET9INLINE static ET9S16 __XDistanceAsQVGA(_ET9AWSPContext *pContext, ET9FLOAT distance)
{
    return (SBYTE2)((distance * __DATA->fKeyboardScaleX) + 0.5f);
}

ET9INLINE static ET9S16 __YDistanceAsQVGA(_ET9AWSPContext *pContext, ET9FLOAT distance)
{
    return (SBYTE2)((distance * __DATA->fKeyboardScaleY) + 0.5f);
}

ET9INLINE static void __GetSwypeKeyCenterQVGA(_ET9AWSPContext *pContext, _ET9Key key, _SWPoint* p)
{
    const ET9KdbAreaInfo * const pAreaInfo = &__DATA->pLayoutInfo->pKeyAreas[__DATA->pbLetterKeyIndices[key]];
    p->x = __XIntCoordAsQVGA(pContext, (ET9S16)pAreaInfo->nCenterX);
    p->y = __YIntCoordAsQVGA(pContext, (ET9S16)pAreaInfo->nCenterY);
}

ET9INLINE static void __GetSwypeKeyBoundsQVGA(_ET9AWSPContext *pContext, _ET9Key key, _SWPoint* topLeft, _SWPoint* botRight)
{
    const ET9KdbAreaInfo * const pAreaInfo = &__DATA->pLayoutInfo->pKeyAreas[__DATA->pbLetterKeyIndices[key]];
    topLeft->x = __XIntCoordAsQVGA(pContext, (ET9S16)pAreaInfo->sRegion.wLeft);
    topLeft->y = __YIntCoordAsQVGA(pContext, (ET9S16)pAreaInfo->sRegion.wTop);
    botRight->x = __XIntCoordAsQVGA(pContext, (ET9S16)pAreaInfo->sRegion.wRight);
    botRight->y = __YIntCoordAsQVGA(pContext, (ET9S16)pAreaInfo->sRegion.wBottom);
}

ET9INLINE static void __GetKeyPairDataQVGA(_ET9AWSPContext *pContext, _ET9Key key1, _ET9Key key2, _SWVector* v)
{
    SWKEYPRDATA* ptrTableScaled = __DATA->psKeyDistances + (key1 * __DATA->wLetterKeyCount + key2);
    v->length = (ptrTableScaled->distance << 1);   /* double */
    v->octant = ptrTableScaled->octant;
    v->sPoint.x = ptrTableScaled->slopeX;
    v->sPoint.y = ptrTableScaled->slopeY;
}

ET9INLINE static ET9U16 __GetKeyPairDistance(_ET9AWSPContext *pContext, _ET9Key key1, _ET9Key key2)
{
    SWKEYPRDATA* ptrTableScaled = __DATA->psKeyDistances + (key1 * __DATA->wLetterKeyCount + key2);
    return ptrTableScaled->distance << 1;   /* double */
}


/* */

ET9PRIVATE _ET9Key ET9FARCALL __SymbolToKey(_ET9AWSPContext *pContext, ET9SYMB symbol)
{
    ET9UINT min = 0;
    ET9UINT past = __DATA->nSortedSymbolCount;
    do {
        ET9UINT mid = (min + past) >> 1;
        ET9SYMB testSymbol = __DATA->pSortedSymbols[mid];
        if (symbol > testSymbol) {
            min = mid + 1;
        } else {
            past = mid;
            if (testSymbol == symbol) {
                return __DATA->pSortedSymbolKeys[mid];
            }
        }
    }
    while (min < past);

    /* Not found */
    return (BYTE1)__DATA->pLayoutInfo->nKeyAreaCount;
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                                                               
 *
 *                                                                 
 *                                                   
 *                                                             
 *                                                                            
 *
 *                                                                   
 */

ET9PRIVATE ET9BOOL ET9FARCALL __AppendSymbolToKeyMap(_ET9AWSPContext *pContext, ET9SYMB newSymbol, _ET9Key newKey, ET9BOOL bRemapOnConflict)
{
    ET9SYMB symbol;
    ET9UINT nTableIndex;
    for (nTableIndex = 0; nTableIndex < __DATA->nSortedSymbolCount; ++nTableIndex) {
        symbol = __DATA->pSortedSymbols[nTableIndex];
        if (newSymbol <= symbol) {
            if (newSymbol == symbol) {
                /* Already present */
                _ET9Key oldKey = __DATA->pSortedSymbolKeys[nTableIndex];
                if (newKey == oldKey) {
                    /* Identical entry */
                    return _TRUE;
                } else {

                    if (!bRemapOnConflict) {

                        /* Resolve known conflicts where a resolution is clear, or bail. */
                        ET9SYMB oldPrimarySymbol = __DATA->pLayoutInfo->pKeyAreas[oldKey].psChars[0];
                        ET9SYMB newPrimarySymbol = __DATA->pLayoutInfo->pKeyAreas[newKey].psChars[0];

                        /* Should we access primary symbols for the two keys? */
                        if ((__DATA->pLayoutInfo->pKeyAreas[oldKey].nCharCount == 0) ||
                            (__DATA->pLayoutInfo->pKeyAreas[oldKey].nCharCount == 0)) {
                                return _FALSE;
                        }

                        if (newSymbol == __SymbGreekCapitalSigma) {

                            /* Greek capital sigma */
                            /* Sigma is the primary candidate for capital sigma, except as the
                             * final letter in an acronym. */
                            if ((oldPrimarySymbol != __SymbGreekSigma) &&
                                (newPrimarySymbol != __SymbGreekSigma)) {
                                    return _FALSE;
                            }

                            /* If both primary keys are sigma we don't know which one to use. */
                            if ((oldPrimarySymbol == __SymbGreekSigma) &&
                                (newPrimarySymbol == __SymbGreekSigma)) {
                                    return _FALSE;
                            }

                            /* Remap the symbol, or leave it alone. */
                            if (newPrimarySymbol == __SymbGreekSigma) {
                                __DATA->pSortedSymbolKeys[nTableIndex] = newKey;
                            }

                            return _TRUE;
                        }

                        {
                            ET9BOOL bOldIsPrimaryCopy = (ET9BOOL)((oldPrimarySymbol == newSymbol) ||
                                (oldPrimarySymbol == _ET9SymToLower(newSymbol, __DATA->dwSwypeWordLocale)) ||
                                (newSymbol == _ET9SymToLower(oldPrimarySymbol, __DATA->dwSwypeWordLocale)));
                            ET9BOOL bNewIsPrimaryCopy = (ET9BOOL)((newPrimarySymbol == newSymbol) ||
                                (newPrimarySymbol == _ET9SymToLower(newSymbol, __DATA->dwSwypeWordLocale)) ||
                                (newSymbol == _ET9SymToLower(newPrimarySymbol, __DATA->dwSwypeWordLocale)));

                            /* Is newSymbol primary on either key? */
                            if (bOldIsPrimaryCopy || bNewIsPrimaryCopy) {
                                /* Is newSymbol primary on only one of the keys? */
                                if (!(bOldIsPrimaryCopy && bNewIsPrimaryCopy)) {

                                    /* Remap the symbol, or leave it alone. */
                                    if (bNewIsPrimaryCopy) {
                                        __DATA->pSortedSymbolKeys[nTableIndex] = newKey;
                                    }

                                    return _TRUE;
                                }
                            }
                            else {
                                /* Neither is primary. Let's assume it's not an important symbol. */
                                return _TRUE;
                            }
                        }

                        if (symbol == ET9KEY_SHIFT) {
                            /* Pick the leftmost shift key */
                            if (__DATA->pLayoutInfo->pKeyAreas[newKey].sRegion.wLeft <
                                __DATA->pLayoutInfo->pKeyAreas[oldKey].sRegion.wLeft) {
                                    __DATA->pSortedSymbolKeys[nTableIndex] = newKey;
                            }
                            return _TRUE;
                        }

                        return _FALSE;
                    }
                    /* Replace the key for the symbol */
                    __DATA->pSortedSymbolKeys[nTableIndex] = newKey;
                    return _TRUE;
                }
            }

            /* Insert symbol here */
            if (__DATA->nSortedSymbolCount < __MAX_SYMBOLS) {
                _ET9ByteMove(&__DATA->pSortedSymbols[nTableIndex + 1], &__DATA->pSortedSymbols[nTableIndex], (__DATA->nSortedSymbolCount - nTableIndex) * sizeof(ET9SYMB));
                _ET9ByteMove(&__DATA->pSortedSymbolKeys[nTableIndex + 1], &__DATA->pSortedSymbolKeys[nTableIndex], (__DATA->nSortedSymbolCount - nTableIndex) * sizeof(_ET9Key));
                __DATA->pSortedSymbols[nTableIndex] = newSymbol;
                __DATA->pSortedSymbolKeys[nTableIndex] = newKey;
                ++__DATA->nSortedSymbolCount;

                return _TRUE;
            }
            else {
                return _FALSE;
            }
        }
    }

    /* Append symbol */
    if (__DATA->nSortedSymbolCount < __MAX_SYMBOLS) {
        __DATA->pSortedSymbols[__DATA->nSortedSymbolCount] = newSymbol;
        __DATA->pSortedSymbolKeys[__DATA->nSortedSymbolCount] = newKey;
        ++__DATA->nSortedSymbolCount;

        return _TRUE;
    }
    else {
        return _FALSE;
    }
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                                                                 
 *                                                                                 
 *                                                    
 *
 *                                                                 
 *
 *                                                              
 */

ET9PRIVATE ET9BOOL ET9FARCALL __InitSymbolToKeyMap(_ET9AWSPContext *pContext)
{
    ET9SYMB symbol, shiftedSymbol;
    ET9UINT nKeyAreaIndex;
    ET9UINT nSymbolConflicts = 0;
    const ET9KdbAreaInfo * const pAreaInfo = __DATA->pLayoutInfo->pKeyAreas;

    __DATA->nSortedSymbolCount = 0;

    /* NOTE: Shifted characters may or may not be present in the definitional arrays */
    /* Add explicitly defined symbols first */
    for (nKeyAreaIndex = 0; nKeyAreaIndex < __DATA->pLayoutInfo->nKeyAreaCount; ++nKeyAreaIndex) {
        _ET9Key key = __DATA->pbLetterKeyIndices[nKeyAreaIndex];
        const ET9KdbAreaInfo * const pIndexArea = &pAreaInfo[key];
        if (pIndexArea->eKeyType != ET9KTSTRING) {
            ET9UINT nCharIndex;
            for (nCharIndex = 0; nCharIndex < pIndexArea->nCharCount; ++nCharIndex) {
                symbol = pIndexArea->psChars[nCharIndex];
                if (!__AppendSymbolToKeyMap(pContext, symbol, key, _FALSE)) {
                    ++nSymbolConflicts;
                }
            }
            for (nCharIndex = 0; nCharIndex < pIndexArea->nShiftedCharCount; ++nCharIndex) {
                shiftedSymbol = pIndexArea->psShiftedChars[nCharIndex];
                if (!__AppendSymbolToKeyMap(pContext, shiftedSymbol, key, _FALSE)) {
                    ++nSymbolConflicts;
                }
            }
        }
    }

    /* Generate uppercase symbols if they weren't present anywhere in the definition */
    for (nKeyAreaIndex = 0; nKeyAreaIndex < __DATA->pLayoutInfo->nKeyAreaCount; ++nKeyAreaIndex) {
        _ET9Key key = __DATA->pbLetterKeyIndices[nKeyAreaIndex];
        const ET9KdbAreaInfo * const pIndexArea = &pAreaInfo[key];
        if (pIndexArea->eKeyType != ET9KTSTRING) {
            ET9UINT nCharIndex;
            for (nCharIndex = 0; nCharIndex < pIndexArea->nCharCount; ++nCharIndex) {
                symbol = pIndexArea->psChars[nCharIndex];
                shiftedSymbol = _ET9SymToUpper(symbol, __DATA->dwSwypeWordLocale);
                if (symbol != shiftedSymbol) {
                    if (!__AppendSymbolToKeyMap(pContext, shiftedSymbol, key, _FALSE)) {
                        ++nSymbolConflicts;
                    }
                }
            }
        }
    }

    if (nSymbolConflicts > 0) {
        __DATA->bInvalidContext = _TRUE;
        pContext->__SPATH_Status.bKDBConfigOk = _FALSE;
        return _FALSE;
    }

    return _TRUE;
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                                                      
 *                                                                       
 *
 *
 *                                                                 
 *
 *             
 */

ET9PRIVATE void ET9FARCALL __CalculateKeyboardSize(_ET9AWSPContext *pContext)
{
    __DATA->wComputedLayoutWidth = (ET9U16)(__DATA->pLayoutInfo-> wLayoutWidth - __DATA->pLayoutInfo->wLeftOfLeftMostKey);
    __DATA->wComputedLayoutHeight = (ET9U16)(__DATA->pLayoutInfo->wLayoutHeight - __DATA->pLayoutInfo->wTopOfTopMostKey);
    __DATA->wLayoutTop = __DATA->pLayoutInfo->wTopOfTopMostKey;
    __DATA->wLayoutLeft = __DATA->pLayoutInfo->wLeftOfLeftMostKey;
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                                  
 *                                             
 *
 *
 *                                                                 
 *
 *             
 */

ET9PRIVATE void ET9FARCALL __InitSwypeKeyboardLetterInfo(_ET9AWSPContext *pContext)
{
    const ET9KdbAreaInfo * const pAreaInfo = __DATA->pLayoutInfo->pKeyAreas;
    ET9UINT nKeyAreaIndex;
    ET9UINT totalCount;

    __DATA->wLetterKeyCount = 0;

    /* Treat all keys except ENTER and those of type ET9KTSTRING as Swypable. */
    for (nKeyAreaIndex = 0; nKeyAreaIndex < __DATA->pLayoutInfo->nKeyAreaCount; ++nKeyAreaIndex) {
        if ((pAreaInfo[nKeyAreaIndex].eKeyType != ET9KTSTRING) &&
            ((pAreaInfo[nKeyAreaIndex].eKeyType != ET9KTFUNCTION) ||
             (pAreaInfo[nKeyAreaIndex].psChars[0] != ET9KEY_RETURN))) {
            __DATA->pbLetterKeyIndices[__DATA->wLetterKeyCount++] = (BYTE1)nKeyAreaIndex;
        }
    }

    totalCount = __DATA->wLetterKeyCount;

    for (nKeyAreaIndex = 0; nKeyAreaIndex < __DATA->pLayoutInfo->nKeyAreaCount; ++nKeyAreaIndex) {
        if ((pAreaInfo[nKeyAreaIndex].eKeyType != ET9KTSTRING) &&
            ((pAreaInfo[nKeyAreaIndex].eKeyType == ET9KTFUNCTION) &&
             (pAreaInfo[nKeyAreaIndex].psChars[0] == ET9KEY_RETURN))) {
            __DATA->pbLetterKeyIndices[totalCount++] = (BYTE1)nKeyAreaIndex;
        }
    }

    ET9Assert(totalCount < ET9_KDB_MAX_KEYS);
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                                                          
 *                                                       
 *
 *                                                                              
 *
 *                                                                                                        
 *                                                                                                           
 *                                                                                                          
 *               
 *
 *
 *                                                                 
 *
 *             
 */

ET9PRIVATE void ET9FARCALL __CalculateKeyboardSwypableArea(_ET9AWSPContext *pContext)
{
    ET9INT nMinX = ET9INT_MAX, nMinY = ET9INT_MAX, nMaxX = ET9INT_MIN, nMaxY = ET9INT_MIN;
    const ET9KdbAreaInfo * const pAreaInfo = __DATA->pLayoutInfo->pKeyAreas;
    ET9UINT nLetterKeyIndex;
    ET9UINT nWidth;
    ET9UINT nHeight;
    ET9INT intRatio;

    for (nLetterKeyIndex = 0; nLetterKeyIndex < __DATA->wLetterKeyCount; ++nLetterKeyIndex) {
        const ET9KdbAreaInfo * const pIndexLetterKey = &pAreaInfo[__DATA->pbLetterKeyIndices[nLetterKeyIndex]];

        if (pIndexLetterKey->sRegion.wLeft < nMinX) nMinX = pIndexLetterKey->sRegion.wLeft;
        if (pIndexLetterKey->sRegion.wTop < nMinY) nMinY = pIndexLetterKey->sRegion.wTop;
        if (pIndexLetterKey->sRegion.wRight > nMaxX) nMaxX = pIndexLetterKey->sRegion.wRight;
        if (pIndexLetterKey->sRegion.wBottom > nMaxY) nMaxY = pIndexLetterKey->sRegion.wBottom;
    }

    nWidth = nMaxX + 1 - nMinX;
    nHeight = nMaxY + 1 - nMinY;

    /* Support converting screen coordinates to keyboard coordinates with negative values */
    __DATA->fKeyboardOffsetX = (ET9FLOAT)nMinX;
    __DATA->fKeyboardOffsetY = (ET9FLOAT)nMinY;

    /* Set up float scales for use with incoming path data */
    __DATA->fKeyboardScaleX = (ET9FLOAT)__wSwypeKeyboardWidthQVGA / (ET9FLOAT)nWidth;
    __DATA->fKeyboardScaleY = (ET9FLOAT)__DATA->wKeyboardHeightQVGA / (ET9FLOAT)nHeight;

    /* Set up numerator/denominator pairs for use by Swype code */
    intRatio = __wSwypeKeyboardWidthQVGA / nWidth;    /* check for exact multiple */
    if ((intRatio * nWidth) == __wSwypeKeyboardWidthQVGA) {
        __DATA->wKeyboardScaleXNumerator = (ET9S16)intRatio;
        __DATA->wKeyboardScaleXDenominator = 1;
    } else {
        __DATA->wKeyboardScaleXNumerator = __wSwypeKeyboardWidthQVGA;
        __DATA->wKeyboardScaleXDenominator = (ET9S16)nWidth;
    }
    intRatio = __DATA->wKeyboardHeightQVGA / nHeight;        /* check for exact multiple */
    if ((intRatio * nHeight) == __DATA->wKeyboardHeightQVGA) {
        __DATA->wKeyboardScaleYNumerator = (ET9S16)intRatio;
        __DATA->wKeyboardScaleYDenominator = 1;
    } else {
        __DATA->wKeyboardScaleYNumerator = __DATA->wKeyboardHeightQVGA;
        __DATA->wKeyboardScaleYDenominator = (ET9S16)nHeight;
    }
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                             
 *
 *
 *                                                                 
 *
 *             
 */

ET9PRIVATE void ET9FARCALL __InitSwypeKeyboardRows(_ET9AWSPContext *pContext)
{
    ET9UINT nRowIndex;
    /* For now, let's assume the rows are of equal height */
    for (nRowIndex = 0; nRowIndex <= __DATA->wKeyboardRowCount; ++nRowIndex) {
        __DATA->pwKeyboardRowBottom[nRowIndex] = (ET9S16)(nRowIndex * __DATA->pLayoutInfo->nMedianKeyHeight);
    }
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                                                     
 *
 *
 *                                                                  
 *
 *             
 */

ET9PRIVATE void ET9FARCALL __CalculateKeyDistanceMatrix(_ET9AWSPContext *pContext)
{
    ET9INT key1;
    for (key1 = 0; key1 < __DATA->wLetterKeyCount; key1++)
    {
        ET9INT key2;
        for (key2 = 0; key2 < __DATA->wLetterKeyCount; key2++)
        {
            SWKEYPRDATA* ptrTableScaled = __DATA->psKeyDistances + (key1 * __DATA->wLetterKeyCount + key2);

            if (key1 == key2)
            {
                ptrTableScaled->distance = 0;
                ptrTableScaled->octant = 255;
                ptrTableScaled->slopeX = 0;
                ptrTableScaled->slopeY = 0;
            }
            else
            {
                _SWPoint x, y;
                _SWVector v;
                __GetSwypeKeyCenterQVGA(pContext, (BYTE1)key1, &x);
                __GetSwypeKeyCenterQVGA(pContext, (BYTE1)key2, &y);
                _SWVector_Construct_PP(&v, &x, &y);

                ptrTableScaled->distance = (BYTE1)(_SWPoint_distance(&x, &y) / 2);   /* divided by 2 to make big keyboards fit */
                ptrTableScaled->octant = v.octant;
                ptrTableScaled->slopeX = (SBYTE1)v.sPoint.x;
                ptrTableScaled->slopeY = (SBYTE1)v.sPoint.y;

                /*
                //BYTE2 A = x.distance(y);
                //BYTE2 B = x.preciseDistance(y) >> PRECISION_ADJ_EXP;
                //BYTE2 C = x.distanceF(y);
                //if ((A < (C - 2)) || (A > (C + 2)))
                //{
                //    wchar_t bufr2[256];
                //    wsprintf(bufr2, L"key pair: [%dx%d]-[%dx%d] distance integer[%d] precise[%d] float[%d]\n",
                //        x.x, x.y, y.x, y.y, A, B, C);
                //    OutputDebugString(bufr2);
                //}
                */
            }
        }
    }
}

/*  */

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                 
 *
 *                                                                              
 *                                                                  
 *
 *                                                                
 */

ET9PRIVATE ET9BOOL ET9FARCALL __InitSwypeKeyboardInfo(ET9AWLingInfo * const pLingInfo, _ET9AWSPContext *pContext)
{
    __DATA->pLayoutInfo = &pLingInfo->pLingCmnInfo->Base.pWordSymbInfo->Private.sCurrKeyboardLayout;
    /* Avoid division by 0 */
    if (__DATA->pLayoutInfo->wLayoutHeight == 0) {
        __DATA->bInvalidContext = _TRUE;
        pContext->__SPATH_Status.bKDBConfigOk = _FALSE;
        return _FALSE;
    }

    __CalculateKeyboardSize(pContext);

    __DATA->wKeyboardRowCount = (ET9U16)((__DATA->wComputedLayoutHeight + __DATA->pLayoutInfo->nMedianKeyHeight / 2) / __DATA->pLayoutInfo->nMedianKeyHeight);
    if ((__DATA->wKeyboardRowCount < __MIN_KEYBOARD_ROWS) || (__DATA->wKeyboardRowCount > __MAX_KEYBOARD_ROWS)) {
        __DATA->bInvalidContext = _TRUE;
        pContext->__SPATH_Status.bKDBConfigOk = _FALSE;
        return _FALSE;
    }
    __DATA->wKeyboardHeightQVGA = __wSwypeKeyboardHeightQVGA[__DATA->wKeyboardRowCount];

    /* Identify keys with symbols that occur in words in databases. */
    __InitSwypeKeyboardLetterInfo(pContext);

    /* Create symbol-to-key map */
    if (!__InitSymbolToKeyMap(pContext)) {
        return _FALSE;
    }

    /* Calculate size of the area containing "letter keys" and set our screen coordinate transform */
    __CalculateKeyboardSwypableArea(pContext);

    /* Calculate the rows and their bottom coordinates */
    __InitSwypeKeyboardRows(pContext);

    /* Calculate distance and angle information for all Swypable key pairs */
    __CalculateKeyDistanceMatrix(pContext);

    /* ScoreWord() uses keyWidth and keyHeight. */
    /* ScoreSuffix(), which we are not currently using in XT9, additionally uses keyboardWidth and keyboardHeight as "large values." */
    /* IPAnalyzer uses bottomRowMidY. */
    _SWScreenGeometry_setScreenGeometry(&__DATA->sBackEnd.sScreenGeometry,
       __YDistanceAsQVGA(pContext, __DATA->pLayoutInfo->nMedianKeyHeight * 8.0f),
       __XDistanceAsQVGA(pContext, __DATA->pLayoutInfo->nMedianKeyWidth * 8.0f),
       __YDistanceAsQVGA(pContext, __DATA->wComputedLayoutHeight),
       __XDistanceAsQVGA(pContext, __DATA->wComputedLayoutWidth),
       __YDistanceAsQVGA(pContext, (ET9FLOAT)__DATA->pLayoutInfo->nMedianKeyHeight / 2.0f),                                                /* Middly Y of top row */
       __YDistanceAsQVGA(pContext, (ET9FLOAT)__DATA->wComputedLayoutHeight - (ET9FLOAT)__DATA->pLayoutInfo->nMedianKeyHeight / 2.0f)); /* Middly Y of bottom row */
    __DATA->sBackEnd.sScreenGeometry.shiftMargin = 0;

    return _TRUE;
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                                           
 *
 *                                                                              
 *                                                                  
 *
 *                                                                
 */

ET9PRIVATE ET9BOOL ET9FARCALL __EnsureLanguageAndKeyboardInitialized(ET9AWLingInfo * const pLingInfo, _ET9AWSPContext *pContext)
{
    ET9WordSymbInfo * const pWordSymbInfo = pLingInfo->pLingCmnInfo->Base.pWordSymbInfo;

    if ((__DATA->dwLdbNum == pLingInfo->pLingCmnInfo->dwLdbNum) &&
        (__DATA->dwFirstLdbNum == pLingInfo->pLingCmnInfo->dwFirstLdbNum) &&
        (__DATA->dwSecondLdbNum == pLingInfo->pLingCmnInfo->dwSecondLdbNum) &&
        (__DATA->dwKdbNum == pWordSymbInfo->Private.sCurrKeyboardLayout.dwKdbNum) &&
        (__DATA->wPageNum == pWordSymbInfo->Private.sCurrKeyboardLayout.wPageNum)) {
            return !__DATA->bInvalidContext;
    }

    /* Start with a positive attitude */
    __DATA->bInvalidContext = _FALSE;
    pContext->__SPATH_Status.bKDBConfigOk = _TRUE;

    /* Remember what we're initializing with */
    __DATA->dwLdbNum = pLingInfo->pLingCmnInfo->dwLdbNum;
    __DATA->dwFirstLdbNum = pLingInfo->pLingCmnInfo->dwFirstLdbNum;
    __DATA->dwSecondLdbNum = pLingInfo->pLingCmnInfo->dwSecondLdbNum;
    __DATA->dwKdbNum = pWordSymbInfo->Private.sCurrKeyboardLayout.dwKdbNum;
    __DATA->wPageNum = pWordSymbInfo->Private.sCurrKeyboardLayout.wPageNum;

    /* Set most likely locale for symbol-to-key map construction shift operations. */
    __DATA->dwSwypeWordLocale = __DATA->pLingInfo->pLingCmnInfo->dwFirstLdbNum;

    /* Set up keyboard values */
    if (!__InitSwypeKeyboardInfo(pLingInfo, pContext)) {
        return _FALSE;
    }

    /* Create the various Swype components. */
    /* Note that order matters, as they depend on each other. */
    if (__DATA->sBackEnd.pContext == NULL) {
        __DATA->sBackEnd.pContext = &__ET9AWSP_MEM;
        __DATA->sBackEnd.m_pDbm = (SWDbm *)__ET9AWSP_malloc(&__ET9AWSP_MEM, sizeof(SWDbm));
        __DATA->sBackEnd.pIPTable = (SWCIPTable *)__ET9AWSP_malloc(&__ET9AWSP_MEM, sizeof(SWCIPTable));
        __DATA->sBackEnd.pIPAnalyzer = (SWCIPAnalyzer *)__ET9AWSP_malloc(&__ET9AWSP_MEM, sizeof(SWCIPAnalyzer));
        __DATA->sBackEnd.m_pSearchDB = (_SWCSearchDB *)__ET9AWSP_malloc(&__ET9AWSP_MEM, sizeof(_SWCSearchDB));

        if ((__DATA->sBackEnd.m_pDbm == 0) ||
            (__DATA->sBackEnd.pIPTable == 0) ||
            (__DATA->sBackEnd.pIPAnalyzer == 0) ||
            (__DATA->sBackEnd.m_pSearchDB == 0)) {
                return _FALSE;
        }

        __DATA->sBackEnd.m_pDbm->m_backend = &__DATA->sBackEnd;
        __DATA->sBackEnd.pIPTable->m_backend = &__DATA->sBackEnd;
        __DATA->sBackEnd.pIPAnalyzer->m_backend = &__DATA->sBackEnd;
        __DATA->sBackEnd.m_pSearchDB->m_backend = &__DATA->sBackEnd;

        SWCIPTable_Construct(__DATA->sBackEnd.pIPTable);
        SWCIPAnalyzer_Construct(__DATA->sBackEnd.pIPAnalyzer);
        _SWCSearchDB_Construct(__DATA->sBackEnd.m_pSearchDB);
    }
    else {
        if (__DATA->sBackEnd.pContext != &__ET9AWSP_MEM) {
            /* We've got a new context */
            __DATA->sBackEnd.pContext = &__ET9AWSP_MEM;
        }
    }

    /* Set up DBM info */
    if (pWordSymbInfo->Private.eStateFreeDoubleLetters == ET9_SettingValue_Auto) {
        __DATA->sBackEnd.m_pDbm->activeLanguageRequiresDoubleLetterGesture = _ET9_LanguageSpecific_IsRequireDoubleLetterGestureLanguage(__DATA->pLingInfo->pLingCmnInfo->dwFirstLdbNum);
    }
    else {
        __DATA->sBackEnd.m_pDbm->activeLanguageRequiresDoubleLetterGesture = (ET9BOOL)(pWordSymbInfo->Private.eStateFreeDoubleLetters == ET9_SettingValue_Off);
    }

    /* The IPTable constructor uses ScreenGeometry's keyRadius and the row count */
    /* to set up values. It needs to be re-initialized when the keyboard changes. */
    SWCIPTable_Initialize(__DATA->sBackEnd.pIPTable);

    /* Notify Swype code of language/keyboard change */
    _SWCSearchDB_KeyboardChanged(__DATA->sBackEnd.m_pSearchDB);

    return _TRUE;
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                       
 *
 *                                                                              
 *
 *             
 */

void ET9FARCALL _ET9AWSP_Init(ET9AWLingInfo * const pLingInfo)
{
    _ET9AWSPContext *pContext = &__ET9AWSP_MEM;

    /* Save the memory area for faux-allocation. */
    __ET9AWSP_MEM.pSpmMemoryFree = (ET9U8*)__ET9AWSP_MEM.pPreAllocMem;

    pContext->pSpmGlobals = 0;
    pContext->pSpmGlobals = (__Swype_Globals *)__ET9AWSP_malloc(&__ET9AWSP_MEM, sizeof(__Swype_Globals));
    if (pContext->pSpmGlobals == 0) {
        return;
    }
    _ET9ClearMem(__DATA, sizeof(__Swype_Globals));

    /* Indicate we don't have any valid values */
    __DATA->dwLdbNum = ET9PLIDNone;
    __DATA->dwFirstLdbNum = ET9PLIDNone;
    __DATA->dwSecondLdbNum = ET9PLIDNone;
    __DATA->dwKdbNum = ET9PLIDNone;
    __DATA->wPageNum = ET9PLIDNone;

    /* Save pLingInfo for Swype callback use (e.g. keyboard definition callbacks) */
    __DATA->pLingInfo = pLingInfo;
}
/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                  
 *
 *                                                                  
 *                                                        
 *                                                        
 *                                                                 
 *                                                                                       
 *
 *             
 */

ET9PRIVATE void ET9FARCALL __ProcessMouseData(_ET9AWSPContext *pContext, ET9S16 wX, ET9S16 wY, ET9U32 dwTimeMS, ET9BOOL bIsSpecial)
{
    _SWPoint sPoint;
    sPoint.x = wX;
    sPoint.y = wY;

    if (__DATA->sBackEnd.pIPAnalyzer && !__DATA->bPanic)
    {
        SWCIPAnalyzer_ProcessMouseData(__DATA->sBackEnd.pIPAnalyzer, dwTimeMS, &sPoint, bIsSpecial);
    }
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                     
 *
 *                                                                              
 *                                                                  
 *
 *                                                         
 */

ET9PRIVATE ET9BOOL ET9FARCALL __AnalyzePath(ET9AWLingInfo * const pLingInfo, _ET9AWSPContext *pContext)
{
    /* pLingInfo->pLingCmnInfo->Base.pWordSymbInfo has sCurrTrace and sCurrKeyboardLayout. */
    ET9WordSymbInfo * const pWordSymbInfo = pLingInfo->pLingCmnInfo->Base.pWordSymbInfo;
    ET9TracePointTime *pTracePointTime;
    ET9UINT nIndex;

    if (pWordSymbInfo->Private.sCurrTrace.nPointCount < 2)
    {
        return _FALSE;
    }

    __DATA->sBackEnd.m_pSearchDB->calledCheckMultipleIPsSequence = _FALSE;

#if __ET9A_DEBUG_MEM_SIZE
    {
        static ET9UINT nMaxPoints = 0;
        if (pWordSymbInfo->Private.sCurrTrace.nPointCount > nMaxPoints) {
            nMaxPoints = pWordSymbInfo->Private.sCurrTrace.nPointCount;
            Log(SWLogger_DEBUG, TEXT("Max trace points so far: %d.\n"), nMaxPoints);
        }
    }
#endif

   /* Send a "next item is PenDown" event */
    __ProcessMouseData(pContext, 0, 0, SW_SPECIAL_MOUSE_EVENT, _TRUE);

    for (nIndex = 0; nIndex < pWordSymbInfo->Private.sCurrTrace.nPointCount - 1; ++nIndex) {
        pTracePointTime = &pWordSymbInfo->Private.sCurrTrace.pPoints[nIndex];
        __ProcessMouseData(pContext,
            (SBYTE2)(0.5f /* round */ + (pTracePointTime->fX - __DATA->fKeyboardOffsetX) * __DATA->fKeyboardScaleX),
            (SBYTE2)(0.5f /* round */ + (pTracePointTime->fY - __DATA->fKeyboardOffsetY) * __DATA->fKeyboardScaleY),
            pTracePointTime->dwTimeMS,
            _FALSE);
    }

    /* Send a "next item is PenUp" event */
    __ProcessMouseData(pContext, 1, 1, SW_SPECIAL_MOUSE_EVENT, _TRUE);

    /* Send final event */
    pTracePointTime = &pWordSymbInfo->Private.sCurrTrace.pPoints[pWordSymbInfo->Private.sCurrTrace.nPointCount - 1];
    __ProcessMouseData(pContext,
        (SBYTE2)(0.5f /* round */ + (pTracePointTime->fX - __DATA->fKeyboardOffsetX) * __DATA->fKeyboardScaleX),
        (SBYTE2)(0.5f /* round */ + (pTracePointTime->fY - __DATA->fKeyboardOffsetY) * __DATA->fKeyboardScaleY),
        pTracePointTime->dwTimeMS,
        _FALSE);

    /* Identify lower bound of top alphabetic row if Qwerty layout with adjacent vowels */
    __DATA->sBackEnd.m_pSearchDB->vowelKeysLowerBound = -1;           /* Init to invalid value */
    __DATA->sBackEnd.m_pSearchDB->vowelKeysLeftBound = -1;            /* Init to invalid value */
    __DATA->sBackEnd.m_pSearchDB->vowelKeysRightBound = -1;           /* Init to invalid value */
    {
        BYTE1 uKey = SWDbm_charToKey1B(__DATA->sBackEnd.m_pDbm, 'u');
        if (uKey != SWDbm_KD_INVALID_CHAR)         /* Not a Latin keyboard if no 'u' assigned */
        {
            /* Next confirm that this is really a Latin keyboard by confirming that the 'u' is the primary character on its key */
            if (SWDbm_getKeyName(__DATA->sBackEnd.m_pDbm, uKey) == (BYTE1)'u')
            {
                BYTE1 oKey = SWDbm_charToKey1B(__DATA->sBackEnd.m_pDbm, 'o');
                BYTE1 iKey = SWDbm_charToKey1B(__DATA->sBackEnd.m_pDbm, 'i');
                if ((oKey != SWDbm_KD_INVALID_CHAR) && (iKey != SWDbm_KD_INVALID_CHAR))
                {
                    _SWPoint minU, minI, minO, maxU, maxI,maxO;
                   __GetSwypeKeyBoundsQVGA(pContext, uKey, &minU, &maxU);
                   __GetSwypeKeyBoundsQVGA(pContext, iKey, &minI, &maxI);
                   __GetSwypeKeyBoundsQVGA(pContext, oKey, &minO, &maxO);
                    if ((maxU.y == maxI.y) && (maxI.y == maxO.y) && ((maxU.x + 1) >= minI.x) && ((maxI.x + 1) >= minO.x))
                    {
                        __DATA->sBackEnd.m_pSearchDB->vowelKeysLowerBound = maxU.y;           /* Set to lower boundary of adjacent vowels */
                        __DATA->sBackEnd.m_pSearchDB->vowelKeysLeftBound = minU.x;            /* Set to left boundary of leftmost vowel */
                        __DATA->sBackEnd.m_pSearchDB->vowelKeysRightBound = maxO.x;           /* Set to right boundary of rightmost vowel */
                    }
                }
            }
        }
    }

    /* Prepare IP tables */
    _SWCSearchDB_CreateSearchIPTable(__DATA->sBackEnd.m_pSearchDB, 0, ALG_ORIG_NEW2, _TRUE, _TRUE);
    __DATA->sBackEnd.m_pSearchDB->goodEnoughScoreCurrent = __DATA->sBackEnd.m_pSearchDB->goodEnoughScore * (float)__DATA->sBackEnd.m_pSearchDB->m_wIPTableCount / (float)1.5;
    __DATA->sBackEnd.m_pSearchDB->bestGoodWordScore = __DATA->sBackEnd.m_pSearchDB->goodEnoughScoreCurrent * (float)1000.0;   /* Arbitrary - result only matters if bestScore < goodEnoughScore */
    _SWCSearchDB_AnalyzePathLength(__DATA->sBackEnd.m_pSearchDB, ALG_ORIG_NEW2, _TRUE);

    __DATA->sBackEnd.m_pSearchDB->finalScoringPass = _TRUE;

    {
        SWCIPTableRow **pIPTable = SWCIPTable_GetIPTableArray(__DATA->sBackEnd.pIPTable);
        SBYTE2 actualIPTableCount = __DATA->sBackEnd.pIPTable->wIPCount;
        ET9BOOL languageRequiresDoubleGesture = _FALSE;
        float pathCurvatureRatio;
#if CAN_REQUIRE_DOUBLE_GESTURE
        languageRequiresDoubleGesture = __DATA->sBackEnd.m_pDbm->activeLanguageRequiresDoubleLetterGesture;
#endif

        /* Set double key penalties to reduced levels UNLESS this appears to be a "two-letter word path" - i.e.
           trying to distinguish between "to" and "too", "se" and "see".  Such paths are relatively straight
           and have only two IPs. */
        pathCurvatureRatio = pIPTable[0]->m_fLengthRatio;
        if (languageRequiresDoubleGesture)
        {
            __DATA->sBackEnd.m_pSearchDB->DoubleKeysToSegmentPenalty = DOUBLE_KEYS_TO_SEGMENT;
            __DATA->sBackEnd.m_pSearchDB->DoubleKeysToSingleIPPenalty = DOUBLE_KEYS_TO_SINGLE_IP;
            __DATA->sBackEnd.m_pSearchDB->DoubleKeysToSingleIPOnly2IPsPenalty = DOUBLE_KEYS_TO_SINGLE_IP_ONLY2_IPS;
            __DATA->sBackEnd.m_pSearchDB->DoubleKeysToSingleIPOnly3IPsPenalty = DOUBLE_KEYS_TO_SINGLE_IP_ONLY3_IPS;
            __DATA->sBackEnd.m_pSearchDB->DoubleKeysToMergedIPPenalty = DOUBLE_KEYS_TO_MERGED_IP;
            __DATA->sBackEnd.m_pSearchDB->ExcessDoubleKeysPenaltyAllowance = EXCESS_DOUBLE_KEYS_PENALTY_ALLOWANCE;
#if CAN_REQUIRE_DOUBLE_GESTURE
            __DATA->sBackEnd.m_pSearchDB->doubleGestureRequired = _TRUE;
#endif
        }
        else if ((actualIPTableCount == 2) && (pathCurvatureRatio < INCREASED_DOUBLE_PENALTY_MAX_CURVATURE_RATIO))
        {
            __DATA->sBackEnd.m_pSearchDB->DoubleKeysToSegmentPenalty = DOUBLE_KEYS_TO_SEGMENT_1;
            __DATA->sBackEnd.m_pSearchDB->DoubleKeysToSingleIPPenalty = DOUBLE_KEYS_TO_SINGLE_IP_1;
            __DATA->sBackEnd.m_pSearchDB->DoubleKeysToSingleIPOnly2IPsPenalty = DOUBLE_KEYS_TO_SINGLE_IP_ONLY2_IPS_1;
            __DATA->sBackEnd.m_pSearchDB->DoubleKeysToSingleIPOnly3IPsPenalty = DOUBLE_KEYS_TO_SINGLE_IP_ONLY3_IPS_1;
            __DATA->sBackEnd.m_pSearchDB->DoubleKeysToMergedIPPenalty = DOUBLE_KEYS_TO_MERGED_IP_1;
            __DATA->sBackEnd.m_pSearchDB->ExcessDoubleKeysPenaltyAllowance = EXCESS_DOUBLE_KEYS_PENALTY_ALLOWANCE_1;
#if CAN_REQUIRE_DOUBLE_GESTURE
            __DATA->sBackEnd.m_pSearchDB->doubleGestureRequired = _FALSE;
#endif
        }
        else
        {
            __DATA->sBackEnd.m_pSearchDB->DoubleKeysToSegmentPenalty = DOUBLE_KEYS_TO_SEGMENT_2;
            __DATA->sBackEnd.m_pSearchDB->DoubleKeysToSingleIPPenalty = DOUBLE_KEYS_TO_SINGLE_IP_2;
            __DATA->sBackEnd.m_pSearchDB->DoubleKeysToSingleIPOnly2IPsPenalty = DOUBLE_KEYS_TO_SINGLE_IP_ONLY2_IPS_2;
            __DATA->sBackEnd.m_pSearchDB->DoubleKeysToSingleIPOnly3IPsPenalty = DOUBLE_KEYS_TO_SINGLE_IP_ONLY3_IPS_2;
            __DATA->sBackEnd.m_pSearchDB->DoubleKeysToMergedIPPenalty = DOUBLE_KEYS_TO_MERGED_IP_2;
            __DATA->sBackEnd.m_pSearchDB->ExcessDoubleKeysPenaltyAllowance = EXCESS_DOUBLE_KEYS_PENALTY_ALLOWANCE_2;
#if CAN_REQUIRE_DOUBLE_GESTURE
            __DATA->sBackEnd.m_pSearchDB->doubleGestureRequired = _FALSE;
#endif
        }
    }

    return !__DATA->bPanic;
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                                                            
 *
 *                                                                  
 *                                                                   
 *                                                                 
 *                                                                                          
 *
 *                                                          
 */

ET9PRIVATE ET9BOOL ET9FARCALL __PrepareSwypeWord(_ET9AWSPContext *pContext, ET9AWPrivWordInfo * const pWord, _SWWord *dbWord, ET9BOOL bRemoveOptionalChars)
{
    /* Set up externalText (max DEFAULT_WORD_SIZE chars)
       Set up keys and keyRepeats */

    ET9UINT nLength = pWord->Base.wWordLen;
    if (nLength >= DEFAULT_WORD_SIZE) {
        return 0;
    }
    if (bRemoveOptionalChars) {
        ET9UINT nIndex;
        ET9UINT nNewLength = 1;
        dbWord->externalText[0] = pWord->Base.sWord[0];
        for (nIndex = 1; nIndex < nLength; ++nIndex) {
            ET9SYMB symbol = pWord->Base.sWord[nIndex];
            if (!(ET9BOOL)_ET9_IsFree(symbol)) {
                dbWord->externalText[nNewLength++] = symbol;
            }
        }
        nLength = nNewLength;
    }
    else {
        _ET9SymCopy(dbWord->externalText, pWord->Base.sWord, nLength);
    }
    dbWord->textLen = nLength;
    dbWord->externalText[nLength] = '\0';
    dbWord->pWord = pWord;

#ifdef ET9_KDB_SWYPE_TEST_MODULE

    if (0 == _ET9symbnicmp(dbWord->externalText, gsExpectedWord, ET9MAXUDBWORDSIZE, __DATA->dwSwypeWordLocale)) {
        gbExpectedWordFound = 1;
    }

#endif

    {
        CPOS keyLength = 0;
        BYTE1 lastKey = SWDbm_KD_INVALID_KEY;
        BYTE2 pathLength = 0;
        BYTE2 doubleKeyPathAllowance = 0;
        BYTE1 *keys = dbWord->keys;
        BYTE1 *keyRepeats = dbWord->keyRepeats;
        BYTE2 pathLen = dbWord->pathLen;
        CPOS idx;
        /* Calculate the keys, key repeats, path length, and apostrophes. */
        for (idx = 0; idx < (CPOS)nLength; idx++)
        {
            ET9SYMB thisChar = dbWord->externalText[idx];
            BYTE1 thisKey = __SymbolToKey(pContext, thisChar);
            if (thisKey >= __DATA->wLetterKeyCount) {
                return 0;
            }
            if (thisKey != lastKey)
            {
                if (pathLen && keyLength) /* Not a valid key pair until first key already processed */
                {
                    pathLength = (BYTE2)(pathLength + __GetKeyPairDistance(pContext, lastKey, thisKey));
                }
                keys[keyLength] = thisKey;
                keyRepeats[keyLength++] = 0;
            }
            else
            {
                keyRepeats[keyLength - 1]++;
                if (pathLen && keyRepeats[keyLength - 1] == 1)     /* Only add allowance for first repeat of key */
                    pathLength = (BYTE2)(pathLength + doubleKeyPathAllowance);
            }
            lastKey = thisKey;
        }

        dbWord->length = keyLength;
        dbWord->pathLen = pathLength;
    }

    dbWord->wordIndex = 0;
    dbWord->freqStem = 3;
    dbWord->freqGroup = 3;
    dbWord->recent = _FALSE;

#if __USE_SWYPE_FREQ
    if (pWord->Body.bIsTop5)
    {
        ET9UINT rank = pWord->Body.dwWordIndex;
        /* Half of 5 percent
           Lower half => group 1, upper half, group 2, etc. */
        ET9UINT wordCount = (__DATA->pLingInfo->pLingCmnInfo->Private.ALdb.header.dwWordCount * 5) / 200;
        ET9UINT frequencyGroup = 4;
        while ((rank < wordCount) && (frequencyGroup < 7)) {
            frequencyGroup++;
            wordCount >>= 1;
        }
        dbWord->freqStem = (SBYTE1)frequencyGroup;
        dbWord->freqGroup = (SBYTE1)frequencyGroup;
    }
    else
    {
        dbWord->freqStem = 3;
        dbWord->freqGroup = 3;
    }
#endif /* __USE_SWYPE_FREQ */

    /* Set the locale for the current language, used in isUpper/Lower,
       toLower/Upper and charToKey(). */
    {
        ET9AWLANGUAGESOURCE eLanguageSource = (ET9AWLANGUAGESOURCE)pWord->Body.bLangIndexScoring;
        dbWord->dwWordLocale = (eLanguageSource == ET9AWSECOND_LANGUAGE) ?
            __DATA->pLingInfo->pLingCmnInfo->dwSecondLdbNum :
            __DATA->pLingInfo->pLingCmnInfo->dwFirstLdbNum;
    }

    return 1;
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                                                            
 *
 *                                                                  
 *                                                                   
 *
 *             
 */

ET9PRIVATE void ET9FARCALL __ScoreWord(_ET9AWSPContext *pContext, ET9AWPrivWordInfo * const pWord)
{
    const ET9BOOL bAllowFreePunct = __DATA->pLingInfo->pLingCmnInfo->Private.ASpc.u.sCmpDataFlex.bAllowFreePunct;
    ET9BOOL bUseFirstScore = _TRUE;
    ET9BOOL bHasOptionalChar = _FALSE;
    ET9BOOL bFirstWordValid = _FALSE;
    ET9BOOL bSecondWordValid = _FALSE;
    ET9FLOAT fScore1 = 0.0f;
    ET9FLOAT fScore2 = 0.0f;
    _SWWord dbWord, dbWordOpt;
    _SWWord_Construct(&dbWord);
    /* Initialize dbWordOpt here to avoid compiler warning about use of uninitialized variable. */
    _SWWord_Construct(&dbWordOpt);

    /* Check for optional characters */
    bHasOptionalChar = _FALSE;
    if (bAllowFreePunct) {
        ET9UINT nIndex;
        for (nIndex = 1; nIndex < pWord->Base.wWordLen; nIndex++) {
            if ((ET9BOOL)_ET9_IsFree(pWord->Base.sWord[nIndex])) {
                bHasOptionalChar = _TRUE;
                break;
            }
        }
    }

    bFirstWordValid = __PrepareSwypeWord(pContext, pWord, &dbWord, _FALSE);

    /* First word form failed, and there won't be a second word form. */
    if (!bFirstWordValid && !bHasOptionalChar) {
        return;
    }

#if 0
    Log(SWLogger_DEBUG, TEXT("Score word: %s\n"), dbWord.externalText);
#endif

    if (bFirstWordValid && (_SWCSearchDB_ScoreWordFromKeyRepeats(__DATA->sBackEnd.m_pSearchDB, &dbWord, _TRUE, ALG_ORIG_NEW2) > 0)) {
#if __USE_SWYPE_FREQ
        fScore1 = dbWord.combinedScore;
#else
        fScore1 = dbWord.combinedScorePathMatch;
#endif
    }
    else {
        bUseFirstScore = _FALSE;
    }

    if (bHasOptionalChar) {
        bSecondWordValid = __PrepareSwypeWord(pContext, pWord, &dbWordOpt, _TRUE);
        if (bSecondWordValid) {
#if 0
            Log(SWLogger_DEBUG, TEXT("Score word (opt): %s\n"), dbWordOpt.externalText);
#endif
            if (_SWCSearchDB_ScoreWordFromKeyRepeats(__DATA->sBackEnd.m_pSearchDB, &dbWordOpt, _TRUE, ALG_ORIG_NEW2) > 0) {
#if __USE_SWYPE_FREQ
                fScore2 = dbWordOpt.combinedScore;
#else
                fScore2 = dbWordOpt.combinedScorePathMatch;
#endif
                if ((fScore2 != 0.0f) && ((fScore2 < fScore1) || (fScore1 == 0.0f))) {
                    bUseFirstScore = _FALSE;
                }
            }
        }
    }

    pWord->Body.xInputScoreSpm = bUseFirstScore ? fScore1 : fScore2;
    /* Log(SWLogger_INFO, TEXT("Score: %0.4f for word '%s'.\n"), pWord->Body.xInputScoreSpm, dbWordOpt.externalText.GetString()); */

    /* Transfer any capitalization changes. Both strings are already terminated. */
    if (__DATA->pLingInfo->pLingCmnInfo->Private.__SPATH_Status.bHasShiftGesture ||
        __DATA->pLingInfo->pLingCmnInfo->Private.__SPATH_Status.bHasAllCapsGesture) {
        ET9BOOL bCapitalized = _FALSE;
        if (bUseFirstScore && bFirstWordValid) {
            ET9UINT nLongIndex;
            for (nLongIndex = 0; nLongIndex < pWord->Base.wWordLen; ++nLongIndex) {
                if (pWord->Base.sWord[nLongIndex] != dbWord.externalText[nLongIndex]) {
                    pWord->Base.sWord[nLongIndex] = dbWord.externalText[nLongIndex];
                    bCapitalized = _TRUE;
                }
            }
        }
        /* Don't apply capitalization to either word if !bUseFirstScore and opt word is unused or failed to "prepare". */
        else if (bSecondWordValid) {
            ET9UINT nLongIndex;
            /* Skip past an initial free symbol in externalText if present.
               There is always at least one symbol in externalText at this point. */
            ET9UINT nShortIndex = (ET9BOOL)_ET9_IsFree(dbWordOpt.externalText[0]) ? 1 : 0;
            for (nLongIndex = 0; nLongIndex < pWord->Base.wWordLen; ++nLongIndex) {
                if (!(ET9BOOL)_ET9_IsFree(pWord->Base.sWord[nLongIndex])) {
                    if (pWord->Base.sWord[nLongIndex] != dbWordOpt.externalText[nShortIndex]) {
                        pWord->Base.sWord[nLongIndex] = dbWordOpt.externalText[nShortIndex];
                        bCapitalized = _TRUE;
                    }
                    ++nShortIndex;
                }
            }
        }
        if (bCapitalized) {
            ++pContext->__SPATH_Status.nSpmModifiedWordCount;
        }
    }
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                                      
 *
 *                                                                  
 *                                                                                                                              
 *
 *             
 */

static void ET9LOCALCALL __SetTopOfShiftGestureMargin(_ET9AWSPContext *pContext, const ET9U16 wTopOfShiftGestureMargin)
{
    __DATA->sBackEnd.sScreenGeometry.shiftMargin = (ET9S16)__YIntCoordAsQVGA(pContext, (ET9S16)wTopOfShiftGestureMargin);
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                              
 *
 *                                                                              
 *
 *                                                        
 */

ET9BOOL ET9FARCALL _ET9AWSP_ScoreWords(ET9AWLingInfo * const pLingInfo)
{
    _ET9AWSPContext *pContext = &__ET9AWSP_MEM;
    ET9AWLingCmnInfo * const pLingCmnInfo = pLingInfo->pLingCmnInfo;
    ET9UINT nLocaleIndex;
    ET9U32 dwLocales[2];

    if ((pContext->pSpmGlobals == 0) || __DATA->bLowMemory) {
        return _FALSE;
    }

    __DATA->bPanic = 0;

    /* Lazily switch language and/or keyboard */
    if (!__EnsureLanguageAndKeyboardInitialized(pLingInfo, pContext)) {
        return _FALSE;
    }

    ET9Assert(pLingCmnInfo->Private.sWordC.pCurrC->nTotalWords > 0);

    __SetTopOfShiftGestureMargin(pContext, pLingCmnInfo->Base.pWordSymbInfo->Private.sCurrKeyboardLayout.wTopOfShiftGestureMargin);

    dwLocales[0] = pLingCmnInfo->dwFirstLdbNum;
    dwLocales[1] = pLingCmnInfo->dwSecondLdbNum;

    /* Analyze path for each language present in word list, and process
       words for one language at a time. */
    for (nLocaleIndex = 0; nLocaleIndex < 2; ++nLocaleIndex) {

        __DATA->dwSwypeWordLocale = dwLocales[nLocaleIndex];
        if (__DATA->dwSwypeWordLocale == 0) {
            continue;
        }

        /* Convert and process the path data.
           This step is locale-dependent, since it maps symbols to keys. */
        if (!__AnalyzePath(pLingInfo, pContext)) {
            return _FALSE;
        }

        {
            ET9UINT nIndex;
            /* Loop over the words and score them */
            for (nIndex = 0; nIndex < pLingCmnInfo->Private.sWordC.pCurrC->nTotalWords; ++nIndex) {
                ET9AWPrivWordInfo * const pIndexWord = &pLingCmnInfo->Private.sWordC.pCurrC->pWordList[pLingCmnInfo->Private.sWordC.pCurrC->pnWordList[nIndex]];
                if ((((ET9AWLANGUAGESOURCE)pIndexWord->Body.bLangIndexScoring != ET9AWSECOND_LANGUAGE) && (nLocaleIndex == 0)) ||
                    (((ET9AWLANGUAGESOURCE)pIndexWord->Body.bLangIndexScoring == ET9AWSECOND_LANGUAGE) && (nLocaleIndex == 1)))
                {
                    __ScoreWord(pContext, pIndexWord);
                    if (__DATA->bPanic) {
                        return _FALSE;
                    }
                }
            }
        }
    }

    {
#if __USE_SWYPE_FREQ
        /* C-F test set optimal values 2.5+ */
        ET9FLOAT fPower = 2.5f;
#else
        /* C-F test set optimal values 1.6 - 1.9
           Sober test set optimal values 1.9 - 3.2
           Liquor splines set optimal values: 1.9
           Wally test set optimal values 1.8 - 1.9 */
        ET9FLOAT fPower = 1.7f;
#endif /* __USE_SWYPE_FREQ */
        ET9UINT nIndex;
        ET9UINT nBestIndex = pLingCmnInfo->Private.sWordC.pCurrC->nTotalWords;
        ET9FLOAT fBestScore = 0;

        for (nIndex = 0; nIndex < pLingCmnInfo->Private.sWordC.pCurrC->nTotalWords; ++nIndex) {

            ET9AWPrivWordInfo * const pIndexWord = &pLingCmnInfo->Private.sWordC.pCurrC->pWordList[pLingCmnInfo->Private.sWordC.pCurrC->pnWordList[nIndex]];

            if (pIndexWord->Body.xInputScoreSpm != 0.0f) {
                pIndexWord->Body.xInputScoreSpm = __SWYPE_MAX_SCORE / _ET9pow_f(pIndexWord->Body.xInputScoreSpm, fPower);
                if (pIndexWord->Body.xInputScoreSpm == 0.0f) {
                    pIndexWord->Body.xInputScoreSpm = __SWYPE_MIN_SCORE;
                }
                else {
                    if (pIndexWord->Body.xInputScoreSpm > __SWYPE_MAX_SCORE) {
                        pIndexWord->Body.xInputScoreSpm = __SWYPE_MAX_SCORE;
                    }
                    if (!pIndexWord->Base.bIsTerm) {

                        _ET9_MakeWordTerm(pLingInfo, pLingCmnInfo->Private.sWordC.pCurrC, nIndex);
                    }
                }
            }
            else {
                pIndexWord->Body.xInputScoreSpm = __SWYPE_MIN_SCORE;
            }
            /* Log(SWLogger_INFO, TEXT("Score: %20.4f, %20.4f for word '%s'.\n"), fSwypeScore, pIndexWord->Body.xInputScoreSpm, pIndexWord->Base.sWord); */
            if (pIndexWord->Body.xInputScoreSpm > fBestScore) {
                fBestScore = pIndexWord->Body.xInputScoreSpm;
                nBestIndex = nIndex;
            }
        }

        if (pContext->nWordIndexInBagWordCount == 0) {
            pContext->nWordIndexInBagMin = nBestIndex;
            pContext->nWordIndexInBagMax = nBestIndex;
        }
        else {
            if (nBestIndex < pContext->nWordIndexInBagMin) {
                pContext->nWordIndexInBagMin = nBestIndex;
            }
            if (nBestIndex > pContext->nWordIndexInBagMax) {
                pContext->nWordIndexInBagMax = nBestIndex;
            }
        }
        pContext->nWordIndexInBagLast = nBestIndex;
        pContext->nWordIndexInBagAccumulator += nBestIndex;
        ++pContext->nWordIndexInBagWordCount;

        /* Log(SWLogger_INFO, TEXT("Average index in bag-of-words: %0.2f (%d, %d - %d).\n"),
            (ET9FLOAT)pContext->nWordIndexInBagAccumulator / (ET9FLOAT)pContext->nWordIndexInBagWordCount,
            nBestIndex,
            pContext->nWordIndexInBagMin,
            pContext->nWordIndexInBagMax); */
    }

    return __USE_SWYPE;
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                              
 *
 *                                                                              
 *                                                       
 *
 *                                                        
 */

void ET9FARCALL _ET9AWSP_SelectWord(ET9AWLingInfo * const pLingInfo, const ET9U8 bWordIndex)
{
    _ET9AWSPContext *pContext = &__ET9AWSP_MEM;

    if ((pContext->pSpmGlobals == 0) || __DATA->bLowMemory) {
        return;
    }
    __DATA->bPanic = 0;

    /* Lazily switch language and/or keyboard */
    if (!__EnsureLanguageAndKeyboardInitialized(pLingInfo, pContext)) {
        return;
    }

    if (bWordIndex != 0) {
        _SWCSearchDB_AdjustWordFrequency(__DATA->sBackEnd.m_pSearchDB, pLingInfo, bWordIndex, _FALSE);
    }
}

/*/*////////////////////// */ */
/* SearchDB callbacks */
ET9BOOL _SWCSearchDB_isVowelKey(_SWCSearchDB *pThis, BYTE1 key, ET9BOOL useActiveKbd)
{
    ET9SYMB symbol;
    const ET9KdbAreaInfo * const pAreaInfo = &_ET9AWSP_DATA->pLayoutInfo->pKeyAreas[_ET9AWSP_DATA->pbLetterKeyIndices[key]];
    ET9_UNUSED(useActiveKbd);
    if (pAreaInfo->nCharCount == 0) return _FALSE;

    /* TODO: Refine this. Should integrate with larger issue of confusables/spelling correction. */
    symbol = _ET9SymToLower(pAreaInfo->psChars[0], _ET9AWSP_DATA->dwSwypeWordLocale);

    switch (symbol)
    {
    case 'a':
    case 'e':
    case 'i':
    case 'o':
    case 'u':
        return _TRUE;
    default:
        return _FALSE;
    }
}

/* Keyboard geometry callbacks */

BYTE1 ET9FARCALL SWDbm_keyCnt(SWDbm *pThis)
{
    return (BYTE1)_ET9AWSP_DATA->pLayoutInfo->nKeyAreaCount;
}

BYTE1 ET9FARCALL SWDbm_letterKeyCnt(SWDbm *pThis)
{
    return (BYTE1)_ET9AWSP_DATA->wLetterKeyCount;
}

BYTE1 ET9FARCALL SWDbm_rowCnt(SWDbm *pThis)
{
    return (BYTE1)_ET9AWSP_DATA->wKeyboardRowCount;
}

/* QVGA coordinates */
BYTE2 ET9FARCALL SWDbm_keyboardUpperBound(SWDbm *pThis)
{
    ET9_UNUSED(pThis);
    return (BYTE2)0;
}

BYTE2 ET9FARCALL SWDbm_keyboardHeight(SWDbm *pThis)
{
    return _ET9AWSP_DATA->wKeyboardHeightQVGA;
}

/* For debugging and for access to vowel keys. Don't worry about non-ASCII characters. */
BYTE1 ET9FARCALL SWDbm_charToKey(SWDbm *pThis, ET9SYMB let)
{
    return __SymbolToKey(__CONTEXT, let);
}

/* For debugging and for access to vowel keys. Don't worry about non-ASCII characters. */
BYTE1 ET9FARCALL SWDbm_charToKey1B(SWDbm *pThis, STRACHAR let)
{
    return __SymbolToKey(__CONTEXT, let);
}

/* For debugging, and to test whether a key's primary letter is one of a small set of vowels. */
ET9SYMB ET9FARCALL SWDbm_getKeyName(SWDbm *pThis, BYTE1 key)
{
    const ET9KdbAreaInfo * const pAreaInfo = &_ET9AWSP_DATA->pLayoutInfo->pKeyAreas[_ET9AWSP_DATA->pbLetterKeyIndices[key]];
    return pAreaInfo->nCharCount == 0 ? '?' : pAreaInfo->psChars[0];
}

/* For debugging, and to test whether a key's primary letter is one of a small set of vowels. */
STRACHAR ET9FARCALL SWDbm_getKeyName1B(SWDbm *pThis, BYTE1 key)
{
    const ET9KdbAreaInfo * const pAreaInfo = &_ET9AWSP_DATA->pLayoutInfo->pKeyAreas[_ET9AWSP_DATA->pbLetterKeyIndices[key]];
    return pAreaInfo->nCharCount == 0 ? '?' : (STRACHAR)pAreaInfo->psChars[0];
}

void ET9FARCALL SWDbm_getKeyCenterScaled(SWDbm *pThis, BYTE1 key, _SWPoint* p)
{
   __GetSwypeKeyCenterQVGA(__CONTEXT, key, p);
}

/*
    // CK Note: Boundary of bottom row is set below keyboard; likewise, the test in PointsInSameRow() allows the
    // lower boundary of the top row to extend above the keyboard.  Thus, points above the keyboard are
    // considered to be in the top row, and points below the keyboard are considered to be in the bottom row.
    for (int i = 0; i < pDbm->rowCnt(); i++) {
        // Note that the top row bound is always zero for now.
        rowBoundary[i] = pDbm->getRowBoundsScaled(i + 1);
    }
    rowBoundary[pDbm->rowCnt() - 1] += 24; // Bonus space for the bottom row.
*/
BYTE2 ET9FARCALL SWDbm_getRowBoundsScaled(SWDbm *pThis, BYTE1 idx)
{
    return _ET9AWSP_DATA->pwKeyboardRowBottom[idx];
}

void ET9FARCALL SWDbm_getKeyPairDataScaled(SWDbm *pThis, BYTE1 key1, BYTE1 key2, _SWVector* v)
{
   __GetKeyPairDataQVGA(__CONTEXT, key1, key2, v);
}

#endif /* ET9_SPM_MODULE */
/*! @} */
/* ----------------------------------< eof >--------------------------------- */
