/*******************************************************************************
;*******************************************************************************
;**                                                                           **
;**                  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: et9aiptable.c                                               **
;**                                                                           **
;**  Description: Inflection point and segment management.                    **
;**                                                                           **
;*******************************************************************************
;******************************************************************************/


/*==========================// */
/*      INCLUDE FILES       // */
/*==========================// */

#include "et9aiptable.h"

#include "et9aipanalyzer.h"
#include "et9asearchdb.h"
#include "et9alogutils.h"


#if __ET9A_DEBUG_MEM_SIZE
static ET9INT nMaxPoolSize = 0;
static ET9INT nMaxArraySize1 = 0;
static ET9INT nMaxArraySize2 = 0;
static ET9INT nMaxArraySize3 = 0;
#endif

#define IPTABLE_MUTEX_EXIST         _TRUE

#define CONVERT_INSTEAD_OF_DELETE                   0
#define CONVERT_PENUP_TO_SOFTUP_NEAR_PRECEDING_IP   1

#define FORCE_MINIMUM_SEGMENT_DISTANCE        0
#define ADJUST_FOR_OVERSHOOT                  1
#define ADJUST_FOR_UNDERSHOOT                 0         /* Removing adjustment for undershoot... */
#if ADJUST_FOR_UNDERSHOOT && !ADJUST_FOR_OVERSHOOT
#error If ADJUST_FOR_UNDERSHOOT set to 1, ADJUST_FOR_OVERSHOOT must also be set to 1
#endif


/*//////////////////////////////////////////////////////////////////// */
/* CIPTable Construction/Destruction */
/*//////////////////////////////////////////////////////////////////// */

void ET9FARCALL SWCIPTable_Initialize(SWCIPTable *pThis)
{
    /* 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. */
    ET9INT i;
    for (i = 0; i < SWDbm_rowCnt(pThis->m_backend->m_pDbm); i++) {
        /* Note that the top row bound is always zero for now. */
        pThis->rowBoundary[i] = SWDbm_getRowBoundsScaled(pThis->m_backend->m_pDbm, (BYTE1)(i + 1));
    }
    pThis->rowBoundary[SWDbm_rowCnt(pThis->m_backend->m_pDbm) - 1] += 24; /* Bonus space for the bottom row. */

    /* Initialize distance thresholds (unweighted) for each IP type and search level */
    /*   Note: This initialization is over-written by reProcessIPTable() for level = 1 */
    pThis->m_backend->m_pSearchDB->IPThresholdFactor = IP_THRESHOLD_FACTOR_LEVEL_INIT;
    pThis->m_backend->m_pSearchDB->SegmentThresholdFactor = SEGMENT_THRESHOLD_FACTOR_LEVEL_INIT;
    SWCIPTable_SetIPThresholdLevels(pThis, pThis->m_backend->m_pSearchDB->IPThresholdFactor, pThis->m_backend->m_pSearchDB->SegmentThresholdFactor);        /* Init thresholds */
}

/*============================================================================= */
/* Function:    SWCIPTable_CIPTable() */
/* Parameters:  None */
/* Description: Construct the IP Table Filler object. */
/*============================================================================= */

void ET9FARCALL SWCIPTable_Construct(SWCIPTable *pThis)
{
    Log(SWLogger_INFO, SWTEXT(" IP Table constructor\n"));

    SWCIPTable_ClearIPTable(pThis);
} /* SWCIPTable_SWCIPTable() */

/*============================================================================= */
/* Function:    SWCIPTable_SetIPThresholdLevels() */
/* Parameters:  float IPFactor, float segmentFactor: Factors (0.0 < factor <= 1.0) */
/*              used to adjust the default (maximum) threshold boundary levels. */
/* Description: Adjust the default (maximum) threshold boundary levels. */
/*============================================================================= */

void ET9FARCALL SWCIPTable_SetIPThresholdLevels(SWCIPTable *pThis, float IPFactor, float segmentFactor)
{
    SBYTE2 level;
    /* Initialize distance thresholds (unweighted) for each IP type and search level */
    /*   Note: This initialization is over-written by reProcessIPTable() for level = 1 */
    /* Currently: 0.67 <= IPFactor <= 0.90 */
    /*            0.65 <= segmentFactor <= 1.00     (as function of Z1Level) */

    pThis->IPthreshold8[PenDown][0] =       (BYTE2)(long)(PenDownThreshold8 * IPFactor);
    pThis->IPthreshold8[PenUp][0] =         (BYTE2)(long)(PenUpThreshold8 * IPFactor);
    pThis->PenUpOvershootIPThreshold8[0] =  (BYTE2)(long)(PenUpOvershoot_IPThreshold8 * IPFactor);
    pThis->PenUpUndershootIPThreshold8[0] = (BYTE2)(long)(PenUpUndershoot_IPThreshold8 * IPFactor);
    pThis->PenDownOvershootIPThreshold8[0] = (BYTE2)(long)(PenDownOvershoot_IPThreshold8 * IPFactor);
    pThis->IPthreshold8[Angle][0] =         (BYTE2)(long)(AngleThreshold8 * IPFactor);
    pThis->IPthreshold8[DoubleLetter][0] =  (BYTE2)(long)(DoubleLetterThreshold8 * IPFactor);
    pThis->IPthreshold8[SoftDown][0] =      (BYTE2)(long)(SoftDownThreshold8 * IPFactor);
    pThis->IPthreshold8[SoftUp][0] =        (BYTE2)(long)(SoftUpThreshold8 * IPFactor);
    pThis->IPthreshold8[Pause][0] =         (BYTE2)(long)(PauseThreshold8 * IPFactor);
    pThis->IPthreshold8[PauseAngle][0] =    (BYTE2)(long)(PauseAngleThreshold8 * IPFactor);
    pThis->IPthreshold8[Multiple][0] =      (BYTE2)(long)(MultipleThreshold8 * IPFactor);
    pThis->IPthreshold8[Exit][0] =          0;             /* Exit and Entry thresholds not used... */
    pThis->IPthreshold8[Entry][0] =         0;
    pThis->IPthreshold8[PathDivision][0] =      0;
    pThis->IPthreshold8[Tap][0] =           (BYTE2)(long)(TapThreshold8 * IPFactor);
    pThis->IPthreshold8[TapHold][0] =       (BYTE2)(long)(TapHoldThreshold8 * IPFactor);

    pThis->SegmentThreshold8[0] =           (BYTE2)(long)(SegmentThreshold8_1 * segmentFactor);
    pThis->PenUpOvershootSegmentThreshold8[0] = (BYTE2)(long)(PenUpOvershoot_SegmentThreshold8_1 * segmentFactor);
    pThis->PenUpUndershootSegmentThreshold8[0] = (BYTE2)(long)(PenUpUndershoot_SegmentThreshold8_1 * segmentFactor);
    pThis->PenDownOvershootSegmentThreshold8[0] = (BYTE2)(long)(PenDownOvershoot_SegmentThreshold8_1 * segmentFactor);
    pThis->PointIPSumLimit[0] =             (BYTE2)(long)(PointIPSumLimit_1 * IPFactor);       /* ??? Use segmentFactor here? */

    for (level = 1; level < MaxSearchLevel; level++)
    {
        pThis->IPthreshold8[PenDown][level] =          (BYTE2)(long)(PenDownThreshold8_2 * IPFactor);
        pThis->IPthreshold8[PenUp][level] =            (BYTE2)(long)(PenUpThreshold8_2 * IPFactor);
        pThis->PenUpOvershootIPThreshold8[level] =     (BYTE2)(long)(PenUpOvershoot_IPThreshold8_2 * IPFactor);
        pThis->PenUpUndershootIPThreshold8[level] =        (BYTE2)(long)(PenUpUndershoot_IPThreshold8_2 * IPFactor);
        pThis->PenDownOvershootIPThreshold8[level] =       (BYTE2)(long)(PenDownOvershoot_IPThreshold8_2 * IPFactor);
        pThis->IPthreshold8[Angle][level] =            (BYTE2)(long)(AngleThreshold8_2 * IPFactor);
        pThis->IPthreshold8[DoubleLetter][level] =     (BYTE2)(long)(DoubleLetterThreshold8_2 * IPFactor);
        pThis->IPthreshold8[SoftDown][level] =         (BYTE2)(long)(SoftDownThreshold8_2 * IPFactor);
        pThis->IPthreshold8[SoftUp][level] =           (BYTE2)(long)(SoftUpThreshold8_2 * IPFactor);
        pThis->IPthreshold8[Pause][level] =            (BYTE2)(long)(PauseThreshold8_2 * IPFactor);
        pThis->IPthreshold8[PauseAngle][level] =       (BYTE2)(long)(PauseAngleThreshold8_2 * IPFactor);
        pThis->IPthreshold8[Multiple][level] =         (BYTE2)(long)(MultipleThreshold8_2 * IPFactor);

        pThis->IPthreshold8[Exit][level] =          0;             /* Exit and Entry thresholds not used... */
        pThis->IPthreshold8[Entry][level] =         0;
        pThis->IPthreshold8[PathDivision][level] =      0;         /* Must be set to 0 since cannot have valid IP match with a PathDivision IP */
        pThis->IPthreshold8[Tap][level] =              (BYTE2)(long)(TapThreshold8_2 * IPFactor);
        pThis->IPthreshold8[TapHold][level] =          (BYTE2)(long)(TapHoldThreshold8_2 * IPFactor);

        pThis->SegmentThreshold8[level] =              (BYTE2)(long)(SegmentThreshold8_2 * segmentFactor);
        pThis->PenUpOvershootSegmentThreshold8[level] = (BYTE2)(long)(PenUpOvershoot_SegmentThreshold8_2 * segmentFactor);
        pThis->PenUpUndershootSegmentThreshold8[level] = (BYTE2)(long)(PenUpUndershoot_SegmentThreshold8_2 * segmentFactor);
        pThis->PenDownOvershootSegmentThreshold8[level] = (BYTE2)(long)(PenDownOvershoot_SegmentThreshold8_2 * segmentFactor);
        pThis->PointIPSumLimit[level] =                (BYTE2)(long)(PointIPSumLimit_2 * IPFactor);    /* ??? Use segmentFactor here? */
    }
    /* Make sure upper limit at least >= lower limit */
    if (pThis->PenUpOvershootIPThreshold8[SearchLevelMaxVal] < pThis->PenUpOvershootIPThreshold8[SearchLevelMinVal])
        pThis->PenUpOvershootIPThreshold8[SearchLevelMaxVal] = pThis->PenUpOvershootIPThreshold8[SearchLevelMinVal];
    if (pThis->PenUpOvershootSegmentThreshold8[SearchLevelMaxVal] < pThis->PenUpOvershootSegmentThreshold8[SearchLevelMinVal])
        pThis->PenUpOvershootSegmentThreshold8[SearchLevelMaxVal] = pThis->PenUpOvershootSegmentThreshold8[SearchLevelMinVal];
    if (pThis->PenUpUndershootIPThreshold8[SearchLevelMaxVal] < pThis->PenUpUndershootIPThreshold8[SearchLevelMinVal])
        pThis->PenUpUndershootIPThreshold8[SearchLevelMaxVal] = pThis->PenUpUndershootIPThreshold8[SearchLevelMinVal];
    if (pThis->PenUpUndershootSegmentThreshold8[SearchLevelMaxVal] < pThis->PenUpUndershootSegmentThreshold8[SearchLevelMinVal])
        pThis->PenUpUndershootSegmentThreshold8[SearchLevelMaxVal] = pThis->PenUpUndershootSegmentThreshold8[SearchLevelMinVal];
    if (pThis->PenDownOvershootIPThreshold8[SearchLevelMaxVal] < pThis->PenDownOvershootIPThreshold8[SearchLevelMinVal])
        pThis->PenDownOvershootIPThreshold8[SearchLevelMaxVal] = pThis->PenDownOvershootIPThreshold8[SearchLevelMinVal];
    if (pThis->PenDownOvershootSegmentThreshold8[SearchLevelMaxVal] < pThis->PenDownOvershootSegmentThreshold8[SearchLevelMinVal])
        pThis->PenDownOvershootSegmentThreshold8[SearchLevelMaxVal] = pThis->PenDownOvershootSegmentThreshold8[SearchLevelMinVal];
}



/*============================================================================= */
/* Function:    SWCIPTable_ClearIPTable() */
/* Parameters:  None */
/* Description: Clear the IP Table data arrays. */
/*============================================================================= */

void ET9FARCALL SWCIPTable_ClearIPTable(SWCIPTable *pThis)
{
    pThis->wIPPoolCount = 0;
    pThis->m_wDoubleIPCount = 0;
    pThis->wIPCount = 0;
    _ET9ClearMem(&pThis->m_IPTableRowOverflow, sizeof(pThis->m_IPTableRowOverflow));
#if DEBUG_SHOW_RECALC_COUNTS
    pThis->IPDistanceCalcCount = pThis->SegDistanceCalcCount = 0;
#endif
    pThis->wPathPointCount = 0;

    pThis->m_penUpDoubleIPIndex = 0;
    pThis->m_ShiftLocCount = 0;
    pThis->m_ShiftAll = _FALSE;
#if ENABLE_DIACRITIC_GESTURES
    pThis->m_DiacriticLocCount = 0;
#endif
    pThis->prevExitIP = pThis->lastExitIP = NULL;

} /* SWCIPTable_ClearIPTable() */



/*============================================================================= */
/* Function:    SWCIPTable_GetIPTableArray() */
/* Parameters:  None */
/* Returns:     SWIPTableRowArray * - Pointer to IP Table data */
/* Description: Return a pointer to our IP Table data array after waiting for a */
/*              mutex to be released. This assures that only one thread will */
/*              be accessing the IP Table array at a time. */
/*============================================================================= */

SWCIPTableRow ** ET9FARCALL SWCIPTable_GetIPTableArray(SWCIPTable *pThis)
{
    return pThis->m_aIPTable;

} /* SWCIPTable_GetIPTableArray() */



/*============================================================================= */
/* Function:    SWCIPTable_GetIPTableArray2() */
/* Parameters:  None */
/* Returns:     SWIPTableRowArray * - Pointer to IP Table data for Double IPs */
/* Description: Return a pointer to our Double IP Table data array.  This does not wait for the */
/*              mutex to be released, and should only be called between successive calls to */
/*              GetIPTableArray()/ReleaseIPTableArray() to assure that only one thread will */
/*              be accessing the IP Table array(s) at a time. */
/*============================================================================= */

SWCIPTableRow ** ET9FARCALL SWCIPTable_GetIPTableArray2(SWCIPTable *pThis)
{
    return pThis->m_aIPTable2;
}



#if DEBUG_SHOW_INFO_WITH_WORD
    static BYTE2 prevWordIndex;
#endif



/*============================================================================= */
/* Function:    SWCIPTable_CheckSignalDetectZ1Input() */
/* Parameters:  None */
/* Description: Determine whether to call SignalDetectZ1Input(), and if so, call it. */
/*============================================================================= */

ET9BOOL ET9FARCALL SWCIPTable_CheckSignalDetectZ1Input(SWCIPTable *pThis)
{
    /* Don't signal Z1 input if this can still be a key-rubbing gesture */
    if (!SWCIPAnalyzer_IsRubbedKeyOK(pThis->m_backend->pIPAnalyzer) && !SWCIPAnalyzer_IsInsideBoundingBoxTap(pThis->m_backend->pIPAnalyzer))
    {
#if DEBUG_TAP_DETECTION
        /*if (pThis->m_backend->pIPAnalyzer->EnablePrediction) */
        {
            Str report;
            report.Format(TEXT(">IP-%d/%d<"), ipData->m_IPType, ipData->m_FixedIndexIn);
            SWStateMachine::AutoSpaceInjectWord(report);
        }
#endif
        return(_TRUE);
    }
    return(_FALSE);
}   /* SWCIPTable_CheckSignalDetectZ1Input(void) */

/*============================================================================= */
/* Function:    SWCIPTable_SetIPTableAnalyzedFlag() */
/* Parameters:  ET9BOOL setClear - true or false value to set in Analyzed flag */
/* Returns:     setClear value */
/*============================================================================= */

ET9BOOL ET9FARCALL SWCIPTable_SetIPTableAnalyzedFlag(SWCIPTable *pThis, ET9BOOL analysisDone)
{
    SWCIPTableRow **pIPTable = SWCIPTable_GetIPTableArray(pThis);   /* Temporarily lock the IP Table array while setting flag */
    if (analysisDone && (pThis->wIPCount > 0))
    {
#if DEBUG_SHOW_INFO_WITH_WORD
        SWWord    *listWord;
        STRWCHAR matchedPrev[3] = { 0 };
        float    prevScore[3] = { 0.0 };
        prevWordIndex = 0;
#endif

        /*SWDbm* pDbm = pThis->m_backend->m_pDbm; */

        /* We have pDbm->mutexDBAccess because all calls ultimately come from */
        /* ProcessMouseData(), which has it. */
        SWCIPTableRow *ipData = pIPTable[0];
        ET9BOOL isZ1Pattern = (ET9BOOL)(ipData->m_IPType != Tap);
        ET9BOOL nullResult = _FALSE;
        pIPTable = NULL;

        if (isZ1Pattern && !nullResult)
        {
            /* Check for and eliminate any anomalous IPs (PathDivision IP in Double IP, etc.) */
            SWCIPTableRow *ipData, *doubleIP;
            SBYTE2 doubleipIndex;
            SBYTE2 table2Size = pThis->m_wDoubleIPCount;
#if DEBUG_IPT6
            SWCIPTable_dumpIPs(pThis, &pThis->m_aIPTable, &pThis->m_aIPTable2, 77, NULL);
#endif
            /* Iterate backwards through double IPs and associated IPs so that indexing iterators remain valid */
            /*   as higher-indexed IPs are deleted */
            for (doubleipIndex = (table2Size - 1); doubleipIndex >= 0; doubleipIndex--)
            {
                SBYTE2 ipIndex;
                doubleIP = pThis->m_aIPTable2[doubleipIndex];
                pIPTable = SWCIPTable_GetIPTableArray(pThis);
                for (ipIndex = doubleIP->gestureIPIndexOut; ipIndex >= doubleIP->gestureIPIndexIn; ipIndex--)
                {
#ifndef DEBUG_CASE_523
                    if (ipIndex < 0) break;
#endif
                    ipData = pIPTable[ipIndex];
                    if (ipData->m_IPType == PathDivision)
                    {
                        /*SWCIPTableRow_erase(__CONTEXT, pIPTable, ipData->m_IPIndex, &pThis->wIPCount);    // Remove preceding IP */
                        SWCIPTable_deleteIP(pThis, ipData, _TRUE, 78);
                    }
                }

                pIPTable = NULL;

                if (doubleIP->gestureIPCount != (doubleIP->gestureIPIndexOut - doubleIP->gestureIPIndexIn + 1))
                {
                    doubleIP->gestureIPCount = doubleIP->gestureIPIndexOut - doubleIP->gestureIPIndexIn + 1;
                }
            }
#if DEBUG_IPT6
            SWCIPTable_dumpIPs(pThis, &pThis->m_aIPTable, &pThis->m_aIPTable2, 79, NULL);
#endif
        }

        /* SavedCodeSegments/205 - Process prediction keys while still in Search thread */
        {
            pThis->m_backend->m_pSearchDB->m_bShiftFirstLetter = _FALSE;
            /* Debug output if we are tracking Fixed data consumption... */
            /*int count = 0; */
            /*pThis->m_backend->pIPAnalyzer->GetStatOfFixedDataPool(count); */
            /*Log(SWLogger::ERROR, L"************* Fixed Data Pool size: %d\n", count); */
            /* send the word list or tapped key to state machine */
        }
    }
    return analysisDone;

} /* SWCIPTable_SetIPTableAnalyzedFlag() */



/*============================================================================= */
/* Function:    SWCIPTable_GetIPTableSize() */
/* Parameters:  (none) */
/* Returns:     SBYTE2 - Number of IPs currently ion IP table */
/*============================================================================= */

SBYTE2 ET9FARCALL SWCIPTable_GetIPTableSize(SWCIPTable *pThis)
{
    return pThis->wIPCount;
}

void ET9FARCALL SWCIPTable_deleteIP(SWCIPTable *pThis, SWCIPTableRow *ipData, ET9BOOL removeIP, SBYTE2 callNum)
{
    SWCIPTableRow *ipDouble, *ipData2;
#if DEBUG_IPT1A
    Log(SWLogger_DEBUG, L" [{%d}]", callNum);
    SWCIPTable_dumpIPType(pThis, ipData);
    Log(SWLogger_DEBUG, L"IP DELETED  @  [%d, %d] {IPPosIndex = %d}\n", ipData->m_IPLocation.x, ipData->m_IPLocation.y, ipData->m_IPPosIndex);
#endif
    ET9_UNUSED(callNum);

    if (removeIP)
    {
#if     DEBUG_IPT1B
        SWCIPTable_dumpIPs(pThis, &pThis->m_aIPTable, &pThis->m_aIPTable2, 87, ipData);
#endif
        SWCIPTableRow **pIPTable = SWCIPTable_GetIPTableArray(pThis);
        SBYTE2  delIndex = ipData->m_IPIndex;
        SBYTE2  ipIndex, doubleIPIndex;
        AlwaysAssert(ipData == pIPTable[delIndex]);
        /* Make all necessary adjustments for the removal of the kIP */
        for (ipIndex = 0; ipIndex < delIndex; ipIndex++)                    /* Adjust ForwardMultipleCount where deleted IP was included */
            if ((ipIndex + pIPTable[ipIndex]->m_ForwardMultipleCount) >= delIndex)
                pIPTable[ipIndex]->m_ForwardMultipleCount--;
        for (ipIndex = pThis->wIPCount - 1; ipIndex > delIndex; ipIndex--)       /* Adjust BackwardMultipleCount where deleted IP was included */
            if ((ipIndex - pIPTable[ipIndex]->m_BackwardMultipleCount) <= delIndex)
                pIPTable[ipIndex]->m_BackwardMultipleCount--;
        for (ipIndex = (delIndex + 1); ipIndex < pThis->wIPCount; ipIndex++)       /* Adjust IP indices above deleted IP */
            pIPTable[ipIndex]->m_IPIndex--;
        for (ipIndex = 0; ipIndex < pThis->wIPCount; ipIndex++)                    /* Adjust Exit indices above deleted IP */
            if (pIPTable[ipIndex]->m_exitIndex > delIndex)
                pIPTable[ipIndex]->m_exitIndex--;
        {
            ET9BOOL deletedIPIncluded = (ET9BOOL)((ipData->m_DoubleIPIndex > 0) && (ipData->m_DoubleIPIndex <= pThis->m_wDoubleIPCount));
            for (doubleIPIndex = 0; doubleIPIndex < pThis->m_wDoubleIPCount; doubleIPIndex++)    /* Adjust double IP included indices above deleted IP */
            {
                ipDouble = pThis->m_aIPTable2[doubleIPIndex];
                if (deletedIPIncluded && (doubleIPIndex == (ipData->m_DoubleIPIndex - 1)))          /* Deleted IP is in this Double IP */
                {
                    if ((ipDouble->gestureIPCount >= 1) && (ipDouble->gestureIPIndexIn <= delIndex) && (ipDouble->gestureIPIndexOut >= delIndex))
                    {
                        if (ipDouble->gestureIPIndexIn == delIndex)
                        {
                            if (ipDouble->gestureIPIndexOut > ipDouble->gestureIPIndexIn)
                            {
                                ipData2 = pThis->m_aIPTable[delIndex + 1];
                                ipDouble->gestureIn = ipData2->m_FixedIndexIn;
                            }
                            else
                                ipDouble->gestureIPCount = 1;
                        }
                        else if (ipDouble->gestureIPIndexOut == delIndex)
                        {
                            if (ipDouble->gestureIPIndexOut > ipDouble->gestureIPIndexIn)
                            {
                                ipData2 = pThis->m_aIPTable[delIndex - 1];
                                ipDouble->gestureOut = ipData2->m_IPPosIndex;
                            }
                            else
                                ipDouble->gestureIPCount = 1;
                        }
                        ipDouble->gestureIPCount--;
                        /* No matter which IP was deleted, the index of the first will remain the same, and the final */
                        /* index will be decremented (due to adjustment of the actual indices following the IP deletion) */
                        ipDouble->gestureIPIndexOut--;
                        if (ipDouble->gestureIPCount == 0)
                        {
                            ipDouble->gestureIPIndexIn = ipDouble->gestureIPIndexOut = -1;
                            ipDouble->gestureIn = ipDouble->gestureOut = -1;
                        }
            #if DEBUG_IPT1A || DEBUG_IPT2
                        Log(SWLogger_DEBUG, L" ****> [{%d}] IP[%d] (", callNum, ipData->m_IPIndex);
                        SWCIPTable_dumpIPType(pThis, ipData);
                        Log(SWLogger_DEBUG, L")  at [%d, %d] {%d} REMOVED from Double IP %d \n",
                            ipData->m_IPLocation.x, ipData->m_IPLocation.y, ipData->m_IPPosIndex, ipData->m_DoubleIPIndex);
            #endif
                    }
                }
                else
                {
                    if (ipDouble->gestureIPIndexIn > delIndex)
                        ipDouble->gestureIPIndexIn--;
                    if (ipDouble->gestureIPIndexOut > delIndex)
                        ipDouble->gestureIPIndexOut--;
                }
            }
        }
        SWCIPTableRow_erase(__CONTEXT, pIPTable, delIndex, &pThis->wIPCount);
    }
#if DEBUG_IPT1B
    if (removeIP)
        SWCIPTable_dumpIPs(pThis, &pThis->m_aIPTable, &pThis->m_aIPTable2, 88, NULL);
#endif
}

/*============================================================================= */
/* Function:    SWCIPTable_includeAdjacentIP() */
/* Parameters:  SWCIPTableRow *ipDouble - Pointer to Double IP to check */
/*              SWCIPTableRow *ipData - Pointer to adjacent IP to check for inclusion */
/* Returns:     true if adjacent IP should be included as part of DoubleLetter gesture sequence */
/*============================================================================= */

ET9BOOL ET9FARCALL SWCIPTable_includeAdjacentIP(SWCIPTableRow *ipDouble, SWCIPTableRow *ipData)
{
    ET9BOOL includeIP;
    if ((ipDouble == NULL) || (ipData == NULL))     /* Sanity check */
        return(_FALSE);
    includeIP = (ET9BOOL)((ipDouble->m_DoubleIPIndex > 0) && (ipData->m_DoubleIPIndex == 0));
    if (!includeIP)
        return(_FALSE);
    includeIP = (ET9BOOL)( (ipData->m_IPType == Angle) || (ipData->m_IPType == PauseAngle) || (ipData->m_IPType == Multiple) ||
                  (ipData->m_IPType == Pause) || (ipData->m_IPType == PenDown)  || (ipData->m_IPType == PenUp)
                  || (ipData->m_IPType == PathDivision));         /* Include so we can identify and delete later... */
    if (!includeIP)
    {
        return(_FALSE);
    }
    includeIP = (ET9BOOL)((ipData->m_FixedIndexOut >= (ipDouble->m_FixedIndexIn - DOUBLE_ANGLE_INCLUDE_MARGIN1)) &&
                 (ipData->m_FixedIndexIn <= (ipDouble->m_FixedIndexOut + DOUBLE_ANGLE_INCLUDE_MARGIN2)));
    if (!includeIP)     /* If adjacent IP is contained within the DoubleLetter gesture), then include it */
        return(_FALSE);

    /* Include the IP if it is "close enough" to the DoubleLetter gesture */
    includeIP = (ET9BOOL)(_SWPoint_distance(&ipDouble->m_IPLocation, &ipData->m_IPLocation) < DOUBLE_ANGLE_INCLUDE_DISTANCE);
    /* Record that this IP should be included in the Double IP, but flag as negative value in case IP is deleted */
    /* before actual insertion in table */
    if (includeIP)
        ipData->m_DoubleIPIndex = -ipDouble->m_DoubleIPIndex;
    return(includeIP);
}




void ET9FARCALL SWCIPTable_setDoubleIPFields(SWCIPTableRow *ipDouble, SWCIPTableRow *ipData)
{
    ipData->m_DoubleIPIndex = ipDouble->m_DoubleIPIndex;
    ipData->gestureLoc = ipDouble->m_IPLocation;
    ipData->gestureIn = ipDouble->m_FixedIndexIn;
    ipData->gestureOut = ipDouble->m_FixedIndexOut;
    ipData->gestureRatio = ipDouble->gestureRatio;
    ipData->circleScore = ipDouble->circleScore;
    ipData->signChangesOK = ipDouble->signChangesOK;
}

void ET9FARCALL SWCIPTable_convertIPtoPauseAngle(SWCIPTableRow *ipData, float adjustmentFactor)
{
    if (ipData->m_IPType == Angle)      /* May happen twice.  Only apply scaling factor on first conversion attempt.  */
    {
#if DEBUG_IPT1A
        Log(SWLogger_DEBUG, L" Angle @ [%d, %d] CONVERTed to PauseAngle\n", ipData->m_IPLocation.x, ipData->m_IPLocation.y);
#endif
        ipData->m_fIPWeight *= adjustmentFactor;
        ipData->m_fSkipPenalty *= adjustmentFactor;
        if (ipData->m_fSkipPenalty > SKIPPED_PAUSEANGLE_IP)
            ipData->m_fSkipPenalty = SKIPPED_PAUSEANGLE_IP;
    }
#if DEBUG_IPT1A
    else
    {
        Log(SWLogger_DEBUG, L" IP of type[%d] @ [%d, %d] CONVERTed to PauseAngle\n", ipData->m_IPType, ipData->m_IPLocation.x, ipData->m_IPLocation.y);
    }
#endif
    ipData->m_IPType = PauseAngle;
    ipData->m_IPFlags = (PAUSE_IP | ANGLE_IP | MULTIPLE_IP);
}

/*============================================================================= */
/* Function:    SWCIPTable_recordIncludedIP() */
/* Parameters:  SWCIPTableRow *ipDouble - Pointer to Double IP to check */
/*              SWCIPTableRow *ipData - Pointer to adjacent IP to check for validity */
/* Returns:     true if adjacent IP should be deleted */
/* Description: Add a new SWCIPTableRow object corresponding to the parameters */
/*============================================================================= */

void ET9FARCALL SWCIPTable_recordIncludedIP(SWCIPTable *pThis, SWCIPTableRow *ipDouble, SWCIPTableRow *ipData, SBYTE2 callID)
{
    (void)callID;

    if ((ipDouble == NULL) || (ipData == NULL))     /* Sanity check */
        return;
    if (ipData->m_DoubleIPIndex != -ipDouble->m_DoubleIPIndex)
        return;
    if (ipData->m_IPIndex >= SWCIPTable_GetIPTableSize(pThis))
        return;
    SWCIPTable_setDoubleIPFields(ipDouble, ipData);

    /* Track number and location of included IPs */
    if ((ipDouble->gestureIn < 0) || (ipDouble->gestureIPCount == 0))        /* Initialized to -1 to show no IPs included yet */
    {
        ipDouble->gestureIn = ipDouble->gestureOut = ipData->m_IPPosIndex;
        ipDouble->gestureIPIndexIn = ipDouble->gestureIPIndexOut = ipData->m_IPIndex;
        ipDouble->gestureIPCount = 1;
    }
    else
    {
#if DEBUG_IPT2
        SWCIPTable_dumpIPs(pThis, &pThis->m_aIPTable, &pThis->m_aIPTable2, 101, ipData);
#endif
        if ((ipData->m_IPPosIndex == ipDouble->gestureIn) || (ipData->m_IPPosIndex == ipDouble->gestureOut))
        {
#if DEBUG_IPT1A || DEBUG_IPT2
            Log(SWLogger_DEBUG, L" {%d}****> DUPLICATE [ IP[%d] (Type %d) at [%d, %d] {%d} NOT ADDED to Double IP %d ", callID, ipData->m_IPIndex, ipData->m_IPType, ipData->m_IPLocation.x, ipData->m_IPLocation.y, ipData->m_IPPosIndex,
                  ipData->m_DoubleIPIndex);
            Log(SWLogger_DEBUG, L" as one of %d (%d - %d) {%d - %d}]\n", ipDouble->gestureIPCount, ipDouble->gestureIPIndexIn, ipDouble->gestureIPIndexOut, ipDouble->gestureIn, ipDouble->gestureOut);
#endif
            return;
        }
        else if (ipData->m_IPPosIndex < ipDouble->gestureIn)
        {
            if (ipDouble->gestureIPIndexIn == ipData->m_IPIndex)    /* If new IP inserted at same index as previous first IP */
                ipDouble->gestureIPIndexOut++;                      /*   then included IPs have shifted up */
            ipDouble->gestureIn = ipData->m_IPPosIndex;
            ipDouble->gestureIPIndexIn = ipData->m_IPIndex;
#if DEBUG_IPT2
            if (m_aIPTable[ipDouble->gestureIPIndexOut]->m_IPPosIndex != ipDouble->gestureOut)
                SWCIPTable_dumpIPs(pThis, &pThis->m_aIPTable, &pThis->m_aIPTable2, 101, ipData);
#endif
            ipDouble->gestureOut = pThis->m_aIPTable[ipDouble->gestureIPIndexOut]->m_IPPosIndex;
            ipDouble->gestureIPCount++;
        }
        else
        {
            if (ipData->m_IPPosIndex > ipDouble->gestureOut)
            {
                ipDouble->gestureOut = ipData->m_IPPosIndex;
                ipDouble->gestureIPIndexOut = ipData->m_IPIndex;
            }
            else
                ipDouble->gestureIPIndexOut++;
            ipDouble->gestureIPCount++;
        }
    }
    if (ipDouble->gestureIPIndexOut > (SWCIPTable_GetIPTableSize(pThis) - 1))
    {
        ipDouble->gestureIPIndexOut = (SWCIPTable_GetIPTableSize(pThis) - 1);
        ipDouble->gestureOut = pThis->m_aIPTable[ipDouble->gestureIPIndexOut]->m_IPPosIndex;
    }
    /* An IP may be added that leaves one or more not-yet-included IPs between the already included ones.  If so, add them now. */
    if (ipDouble->gestureIPCount != (ipDouble->gestureIPIndexOut - ipDouble->gestureIPIndexIn + 1))
    {
#if DEBUG_IPT1A || DEBUG_IPT2
        Log(SWLogger_DEBUG, L" {%d}****> [Double IP[%d] at [%d, %d] {%d} ", callID, ipDouble->m_IPIndex, ipDouble->m_IPLocation.x, ipDouble->m_IPLocation.y, ipDouble->m_IPPosIndex);
        Log(SWLogger_DEBUG, L" has gestureIPCount = %d BUT mismatched included IPs: (%d - %d) {%d - %d}]\n", ipDouble->gestureIPCount, ipDouble->gestureIPIndexIn, ipDouble->gestureIPIndexOut, ipDouble->gestureIn, ipDouble->gestureOut);
#endif
        if (ipDouble->gestureIPCount < (ipDouble->gestureIPIndexOut - ipDouble->gestureIPIndexIn + 1))
        {
            ET9BOOL checkAnother = _TRUE;
            SWCIPTableRow **pIPTable = SWCIPTable_GetIPTableArray(pThis);
            SWCIPTableRow *ipCheck;
            SBYTE2 checkLimit = sw_min(ipDouble->gestureIPIndexOut, (SWCIPTable_GetIPTableSize(pThis) - 1));
            SBYTE2 ipIndex;
            for (ipIndex = ipDouble->gestureIPIndexIn; (ipIndex <= checkLimit) && checkAnother; ipIndex++)
            {
                ipCheck = pIPTable[ipIndex]; /* get IP */
                checkAnother = (ET9BOOL)((ipCheck->m_DoubleIPIndex == 0) || ((ipCheck->m_DoubleIPIndex == ipDouble->m_DoubleIPIndex)));
                if (ipCheck->m_DoubleIPIndex == 0)
                {
                    SWCIPTable_setDoubleIPFields(ipDouble, ipCheck);
                    ipDouble->gestureIPCount++;
#if DEBUG_IPT1A || DEBUG_IPT2
                    Log(SWLogger_DEBUG, L" {%d}****> [ IP[%d] (Type %d) at [%d, %d] {%d} ", callID, ipCheck->m_IPIndex, ipCheck->m_IPType, ipCheck->m_IPLocation.x, ipCheck->m_IPLocation.y, ipCheck->m_IPPosIndex);
                    Log(SWLogger_DEBUG, L" ADDED to Double IP %d as one of %d (%d - %d) {%d - %d}]\n", ipCheck->m_DoubleIPIndex, ipDouble->gestureIPCount, ipDouble->gestureIPIndexIn, ipDouble->gestureIPIndexOut, ipDouble->gestureIn, ipDouble->gestureOut);
#endif
                }
            }
        }
        {
            SBYTE2 GestIPCount = ipDouble->gestureIPIndexOut - ipDouble->gestureIPIndexIn + 1;
            if (ipDouble->gestureIPCount != GestIPCount) /* Update the value of gestureIPCount */
            {
                if (GestIPCount < 1)        /* Indexes gone awry - reset to single IP */
                {
                    ipDouble->gestureIPIndexOut = ipDouble->gestureIPIndexIn;
                    if (ipDouble->gestureIPIndexIn >= 0)
                    {
                        ipDouble->gestureOut = pThis->m_aIPTable[ipDouble->gestureIPIndexOut]->m_IPPosIndex;
                        GestIPCount = 1;
                    }

                    else
                    {
                        GestIPCount = 0;
                    }
                }
                ipDouble->gestureIPCount = GestIPCount;
            }
        }
    }
#if DEBUG_IPT1A || DEBUG_IPT2
    Log(SWLogger_DEBUG, L" {%d}****> [ IP[%d] (Type %d) at [%d, %d] {%d} ", callID, ipData->m_IPIndex, ipData->m_IPType, ipData->m_IPLocation.x, ipData->m_IPLocation.y, ipData->m_IPPosIndex);
    Log(SWLogger_DEBUG, L" ADDED to Double IP %d as one of %d (%d - %d) {%d - %d}]\n", ipData->m_DoubleIPIndex, ipDouble->gestureIPCount, ipDouble->gestureIPIndexIn, ipDouble->gestureIPIndexOut, ipDouble->gestureIn, ipDouble->gestureOut);
#endif
}

#if DEBUG_IPT
static SWCIPTableRow    *ipDump[32];

void SWCIPTable_dumpIPType(SWCIPTable *pThis, SWCIPTableRow *ipData)
{
    switch(ipData->m_IPType)
    {
    case PenDown:
        Log(SWLogger_DEBUG, TEXT("      PenDown "));
        break;
    case PenUp:
        Log(SWLogger_DEBUG, TEXT("        PenUp "));
        break;
    case Angle:
        Log(SWLogger_DEBUG, TEXT("        Angle "));
        break;
    case DoubleLetter:
        Log(SWLogger_DEBUG, TEXT(" DoubleLetter "));
        break;
    case SoftDown:
        Log(SWLogger_DEBUG, TEXT("     SoftDown "));
        break;
    case SoftUp:
        Log(SWLogger_DEBUG, TEXT("       SoftUp "));
        break;
    case Pause:
        Log(SWLogger_DEBUG, TEXT("        Pause "));
        break;
    case PauseAngle:
        Log(SWLogger_DEBUG, TEXT("   PauseAngle "));
        break;
    case Exit:
        Log(SWLogger_DEBUG, TEXT("         Exit "));
        break;
    case Entry:
        Log(SWLogger_DEBUG, TEXT("        Entry "));
        break;
    case Multiple:
        Log(SWLogger_DEBUG, TEXT("     Multiple "));
        break;
    case PathDivision:
        Log(SWLogger_DEBUG, TEXT(" PathDivision "));
        break;
    case Tap:
        Log(SWLogger_DEBUG, TEXT("          Tap "));
        break;
    case TapHold:
        Log(SWLogger_DEBUG, TEXT("      TapHold "));
        break;
    default:
        Log(SWLogger_DEBUG, TEXT(" UNKNOWN TYPE = %d !!!"), ipData->m_IPType);
        break;
    }
}


void ET9FARCALL SWCIPTable_dumpIPs(SWCIPTable *pThis, SWIPTableRowArray* pIPTable, SWIPTableRowArray* pIPTable2, SBYTE2 callNum, SWCIPTableRow  *ipData)
{
    /*SWIPTableRowArray* pIPTable; */
    SBYTE2          ipIndx, dwSize;
    SBYTE4          timeBase = 0, timeOffset;
    ET9BOOL            aboveKeyboard = _FALSE;
    ET9BOOL            hasPenUp = _FALSE;

    SWCIPTable_GetIPTableArray(pThis);   /* Temporarily get the IP Table array */
    dwSize = (SBYTE2)SWIPTableRowArray_size(pIPTable); /* get the size of the array */
    Log(SWLogger_DEBUG, TEXT(" \r\n{IP Table Dump: %d}\n"), callNum);
    for (ipIndx = 0; ipIndx < dwSize; ipIndx++)
    {
        ipDump[ipIndx] = pIPTable[ipIndx];   /* get IP */
        if (ipIndx == 0)
            timeBase = (SBYTE4)ipDump[ipIndx]->m_TimeOffset;
        timeOffset = (SBYTE4)ipDump[ipIndx]->m_TimeOffset - timeBase;
        /*SBYTE2 posIndex = ipDump[ipIndx]->m_IPPosIndex; */
/*///        AlwaysAssert(ipDump[ipIndx]->m_IPIndex == ipIndx); */
        Log(SWLogger_DEBUG, TEXT(" {%d}: IP[%d]:"), ipIndx, ipDump[ipIndx]->m_IPIndex);
        SWCIPTable_dumpIPType(pThis, ipDump[ipIndx]);
        {
            Log(SWLogger_DEBUG, TEXT(" Loc: (%d, %d) Fixed: %d SegLen: %d  Dist: %d  LenRatio: %f  LenFactor: %f  "),
                ipDump[ipIndx]->m_IPLocation.x, ipDump[ipIndx]->m_IPLocation.y, ipDump[ipIndx]->m_IPPosIndex, ipDump[ipIndx]->m_SegmentLen8,
                ipDump[ipIndx]->m_LineLen8, ipDump[ipIndx]->m_fLengthRatio, ipDump[ipIndx]->m_fLengthFactor);
        }
        Log(SWLogger_DEBUG, TEXT(" Fwd:%d Back:%d dIP: %d  Penalty: %f\n"),
            ipDump[ipIndx]->m_ForwardMultipleCount, ipDump[ipIndx]->m_BackwardMultipleCount, ipDump[ipIndx]->m_DoubleIPIndex, ipDump[ipIndx]->m_fSkipPenalty);
        if (ipDump[ipIndx]->m_IPLocation.x < 0)
            aboveKeyboard = _TRUE;
        if (ipDump[ipIndx]->m_IPType == PenUp)
            hasPenUp = _TRUE;
    }
    if (ipData != NULL)
    {
        if (dwSize > 0)
            timeOffset = (SBYTE4)ipData->m_TimeOffset - timeBase;
        else
            timeOffset = (SBYTE4)ipData->m_TimeOffset;
        SBYTE2 posIndex = ipData->m_IPPosIndex;
        Log(SWLogger_DEBUG,TEXT("+: [%d]   %d   (%d, %d)  %d/%d/%d   {%d}  [%d]  {%d}  %d  [%d/%d]  {%d} [%d]\r\n"),
            ipData->m_IPIndex, ipData->m_IPType, ipData->m_IPLocation.x, ipData->m_IPLocation.y,
            ipData->m_FixedIndexIn, posIndex, ipData->m_FixedIndexOut, ipData->m_DoubleIPIndex, ipData->m_exitIndex,
            timeOffset, ipData->m_MergeCount, ipData->m_SegmentLen8, ipData->m_LineLen8, (SWCIPAnalyzer_GetFixedData(pThis->m_backend->pIPAnalyzer, posIndex, 58))->D2Sum,
            ipData->debugData);
    }
    if (SWIPTableRowArray_size(pIPTable2) > 0)
    {
        {
            Log(SWLogger_DEBUG, TEXT(" Double IPs:\n  [Idx]    Type   ( x, y ) {SC}   [gRatio/score]    In/Pos/Out {dIP} (gCnt/In/Out) [exitInd] mrgCnt [segLen/lineLen]  \n"));
        }
        for (ipIndx = 0; ipIndx < (SBYTE2)SWIPTableRowArray_size(pIPTable2); ipIndx++)
        {
            ipDump[ipIndx] = SWIPTableRowArray_getat(pIPTable2, ipIndx);   /* get IP */
            if ((dwSize == 0) && (ipIndx == 0))
                timeBase = (SBYTE4)ipDump[ipIndx]->m_TimeOffset;
            timeOffset = (SBYTE4)ipDump[ipIndx]->m_TimeOffset - timeBase;
            /*SBYTE2 posIndex = ipDump[ipIndx]->m_IPPosIndex; */
            Log(SWLogger_DEBUG, TEXT(" %d: [%d]"),
                ipIndx, ipDump[ipIndx]->m_IPIndex);
            SWCIPTable_dumpIPType(pThis, ipDump[ipIndx]);
            Log(SWLogger_DEBUG, TEXT("(%d, %d) {%d} [%f/%f] "),
                ipDump[ipIndx]->m_IPLocation.x, ipDump[ipIndx]->m_IPLocation.y,
                ipDump[ipIndx]->signChangesOK, ipDump[ipIndx]->gestureRatio, ipDump[ipIndx]->circleScore);
            Log(SWLogger_DEBUG, TEXT(" %d/%d/%d  {%d}    (%d/%d(%d)/"),
                ipDump[ipIndx]->m_FixedIndexIn, ipDump[ipIndx]->m_IPPosIndex, ipDump[ipIndx]->m_FixedIndexOut, ipDump[ipIndx]->m_DoubleIPIndex,
                ipDump[ipIndx]->gestureIPCount, ipDump[ipIndx]->gestureIPIndexIn, ipDump[ipIndx]->gestureIn);
            {
                Log(SWLogger_DEBUG, TEXT(" %d(%d))   [%d]     %d      [%d/%d] \n"),
                   ipDump[ipIndx]->gestureIPIndexOut, ipDump[ipIndx]->gestureOut,
                   ipDump[ipIndx]->m_exitIndex, ipDump[ipIndx]->m_MergeCount, ipDump[ipIndx]->m_SegmentLen8, ipDump[ipIndx]->m_LineLen8);
            }
        }
    }
    Log(SWLogger_DEBUG, TEXT(" \n"));
}
#endif

/*============================================================================= */
/* Function:    SWCIPTable::AddIPtoTable2() */
/* Parameters:  *ipData - new IP to be added to Table2 */
/* Returns:     IPT_OK if a new IP was added in response to the call;IPT_FAILURE otherwise */
/* Description: Add a new SWCIPTableRow object corresponding to the parameters */
/*============================================================================= */

SBYTE2 ET9FARCALL SWCIPTable_AddIPtoTable2(SWCIPTable *pThis, SWCIPTableRow *ipData)
{
    SBYTE2 exitXpos;
    ET9BOOL noPathExit = (ET9BOOL)(SWCIPTable_PathExitsKeyboard(pThis, ipData->m_FixedIndexIn, ipData->m_FixedIndexOut, &exitXpos) == 0);
    if (noPathExit &&
        (pThis->m_wDoubleIPCount < _SWYPE_MAX_DOUBLE_LETTER_IPS) &&
        (ipData != &pThis->m_IPTableRowOverflow))
    {
        pThis->m_aIPTable2[pThis->m_wDoubleIPCount++] = ipData;
        ipData->m_IPIndex = ipData->m_DoubleIPIndex = pThis->m_wDoubleIPCount;  // TODO: Is this correct?
        return(IPT_OK);
    }
    return(IPT_FAILURE);
}

/*============================================================================= */
/* Function:    SWCIPTable_AddIPtoTable() */
/* Parameters:  SWCIPTableRow *ipData - New IP to be added to Table */
/* Returns:     IPT_OK if a new IP was added in response to the call; IPT_FAILURE otherwise */
/* Description: Add a new SWCIPTableRow object corresponding to the parameters */
/*============================================================================= */

SBYTE2 ET9FARCALL SWCIPTable_AddIPtoTable(SWCIPTable *pThis, SWCIPTableRow *ipData)
{
    SWCIPTableRow   **pIPTable;
    SWCIPTableRow   *ipPrev, *ipPrev2, *ipFollow, *ipCheck, *ipDouble;
    SBYTE2          prevIndex, followIndex, checkIndex;
    float           anglePenaltyAdjust;
    SBYTE2          swapPrev = 0;
    ET9BOOL         ipDataInDouble = _FALSE, ipPrevInDouble = _FALSE, ipFollowInDouble = _FALSE, includeNewIPinDouble = _FALSE;
    ET9BOOL         specialIP;
    SWDbm* pDbm = pThis->m_backend->m_pDbm;

    if (ipData == &pThis->m_IPTableRowOverflow) {
        return IPT_FAILURE;
    }

    ipPrev = ipPrev2 = ipFollow = NULL;
#if DEBUG_IPT1A
    SWCIPTable_dumpIPs(pThis, &pThis->m_aIPTable, &pThis->m_aIPTable2, 1, ipData);
#endif
    pIPTable = SWCIPTable_GetIPTableArray(pThis);           /* Temporarily get the IP Table array */
    /* Constrain total number of IPs created for a single path */
    /* Don't add IP in excess of allowed maximum number.  */
    /* Add this IP anyway if it is the PenDown, PenUp, or Tap, so that path processing is completed */
    specialIP = (ET9BOOL)((ipData->m_IPType == PenDown) || (ipData->m_IPType == Tap) || (ipData->m_IPType == PenUp) ||
                         (ipData->m_IPType == SoftUp) || (ipData->m_IPType == TapHold));
    if (!specialIP && (pThis->wIPCount >= MAX_PATH_IP_COUNT))
    {
        SWCIPTable_deleteIP(pThis, ipData, _FALSE, 23);     /* Delete now since not added to table */
#ifdef DEBUG_IPT1A
        SWCIPTable_dumpIPs(pThis, &pThis->m_aIPTable, &pThis->m_aIPTable2, 53, NULL);
#endif
        if (pThis->m_backend->pIPAnalyzer != NULL)
        {
            if (!SWCIPAnalyzer_HasMaxFixedCount(pThis->m_backend->pIPAnalyzer))
            {
                SWCIPAnalyzer_SetMaxFixedCount(pThis->m_backend->pIPAnalyzer, _TRUE);
            }
        }
        return IPT_FAILURE;
    }
    prevIndex = ipData->m_IPIndex - 1;
    AlwaysAssert (ipData->m_IPIndex <= pThis->wIPCount);
    followIndex = 0;
    if (pThis->m_wDoubleIPCount > 0)
        ipDouble = pThis->m_aIPTable2[pThis->m_wDoubleIPCount - 1];
    else
        ipDouble = NULL;
#if DEBUG_IPT
    /* Validate value of m_FixedIndexOut  (CK: This can be eliminated if never seen...) */
    if (!AlwaysAssert(ipData->m_FixedIndexOut >= 0))
        Log(SWLogger_DEBUG,TEXT("IP[%d]  Type: %d  at (%d, %d)  FixedIndexOut = %d !!!/r/n"), ipData->m_IPIndex, ipData->m_IPType,
                         ipData->m_IPLocation.x, ipData->m_IPLocation.y, ipData->m_FixedIndexOut);
#endif
    /* Get previous IP if this is not a PenDown, Tap or TapHold IP... */
    if ((ipData->m_IPType != PenDown) && (ipData->m_IPType != Tap) && (ipData->m_IPType != TapHold) && (prevIndex >= 0))
        ipPrev = pIPTable[prevIndex];  /* get preceding IP */
    /* Check whether IP should be included in last Double IP if this is not a PenDown, PathDivision, Tap or Exit IP... */
    if ((ipData->m_IPType != PenDown) && (ipData->m_IPType < Exit))
    {
        /* Check for any Angle IPs that may have been added after recognizing DoubleLetter gesture */
        if (ipDouble != NULL)
        {
            includeNewIPinDouble = SWCIPTable_includeAdjacentIP(ipDouble, ipData);
#if DEBUG_IPT1A || DEBUG_IPT2
            if (includeNewIPinDouble)
            {
                Log(SWLogger_DEBUG, L" Flag set to include type %d IP at [%d, %d] {%d} ** FOLLOWING ** DoubleLetter IP[%d]\n", ipData->m_IPType, ipData->m_IPLocation.x, ipData->m_IPLocation.y, ipData->m_IPPosIndex, ipDouble->m_DoubleIPIndex);
#if DEBUG_IPT1A
                SWCIPTable_dumpIPs(pThis, &pThis->m_aIPTable, &pThis->m_aIPTable2, 2, NULL);
#endif
            }
            else
                Log(SWLogger_DEBUG, L" Flag NOT set to include type %d IP at [%d, %d] {%d} ** FOLLOWING ** DoubleLetter IP[%d]\n", ipData->m_IPType, ipData->m_IPLocation.x, ipData->m_IPLocation.y, ipData->m_IPPosIndex, ipDouble->m_DoubleIPIndex);
#endif
            /* Check for any Angle IPs that may have been added prior to recognizing DoubleLetter gesture */
            checkIndex = prevIndex;
            ipCheck = ipPrev;
            while ((checkIndex >= 0) && ((ipCheck->m_DoubleIPIndex == ipDouble->m_DoubleIPIndex) || SWCIPTable_includeAdjacentIP(ipDouble, ipCheck)))
            {
                AlwaysAssert(ipCheck == pIPTable[checkIndex]);   /* Make sure ipCheck set correctly */
                if (ipCheck->m_DoubleIPIndex != ipDouble->m_DoubleIPIndex)
                {
                    SWCIPTable_recordIncludedIP(pThis, ipDouble, ipCheck, 4);
#if DEBUG_IPT1A
                    if (ipCheck->m_IPType == PenDown)
                    {
                        Log(SWLogger_DEBUG, L" Including PenDown IP => (dIP = %d) @  [%d, %d] in Double IP[%d]\n", ipCheck->m_DoubleIPIndex, ipCheck->m_IPLocation.x, ipCheck->m_IPLocation.y, ipDouble->m_DoubleIPIndex);
                    }
                    else
                    {
                        Log(SWLogger_DEBUG, L" Including type %d IP at [%d, %d] **PRECEDING** DoubleLetter IP[%d]\n", ipCheck->m_IPType, ipCheck->m_IPLocation.x, ipCheck->m_IPLocation.y, ipDouble->m_DoubleIPIndex);
                    }
                    SWCIPTable_dumpIPs(pThis, &pThis->m_aIPTable, &pThis->m_aIPTable2, 3, NULL);
#endif
                }
                checkIndex--;                   /* Reset ipCheck to next preceding IP */
                if (checkIndex >= 0)
                    ipCheck = pIPTable[checkIndex];        /* get (new) preceding IP */
            }
        }
    }
    if ((ipData->m_IPType != PenDown) && ((ipData->m_IPType < Exit) || (ipData->m_IPType == Multiple) || (ipData->m_IPType == PathDivision)))
    {
        /* We may get one or more IPs occurring just prior to an Exit IP, such that the IP is not detected */
        /* until after the Exit IP (or even the following Entry) has already been added (because the */
        /* second-difference interval used to detect the Angle IP extends beyond the Exit IP location). */
        /* If so, flag this case and insert the IP at the proper position in the table.  In some cases, the */
        /* IP and Exit IP may be detected at exactly the same point. */
        while ((ipData->m_FixedIndexIn <= ipPrev->m_FixedIndexIn) && (prevIndex >= 1))
        {
            /* Sanity check - Make sure same IP is not added twice... */
            if ((ipData->m_FixedIndexIn == ipPrev->m_FixedIndexIn) && (ipData->m_TimeOffset == ipPrev->m_TimeOffset) && (ipPrev->m_IPType != Exit))
            {
                if ((((ipData->m_IPType != PenUp) && (ipData->m_IPType != SoftUp)) || (ipPrev->m_IPType == PenUp)) &&
                     (((ipData->m_IPType == Pause) || (ipData->m_IPType == PauseAngle)) && (ipPrev->m_IPType != Pause) && (ipPrev->m_IPType != PauseAngle))
                    )
                {
#if DEBUG_IPT1A
                    Log(SWLogger_DEBUG, L" Deleting type %d IP @ [%d, %d] - DUPLICATE of preceding IP %d\n", ipData->m_IPType, ipData->m_IPLocation.x, ipData->m_IPLocation.y, prevIndex);
                    SWCIPTable_dumpIPs(pThis, &pThis->m_aIPTable, &pThis->m_aIPTable2, 17, ipData);
#endif
                    SWCIPTable_deleteIP(pThis, ipData, _FALSE, 15);      /* Don't add IP outside of top of keyboard. Delete now since not added to table */
                    return IPT_FAILURE;
                }
                else
                {
                    AlwaysAssert(ipPrev == pIPTable[prevIndex]); /* Make sure ipPrev set correctly for deletion */
#if DEBUG_IPT1A
                    Log(SWLogger_DEBUG, L" Deleting type %d IP at [%d, %d] - DUPLICATE of PenUp IP.\n", ipPrev->m_IPType, ipPrev->m_IPLocation.x, ipPrev->m_IPLocation.y);
#endif
                    SWCIPTable_deleteIP(pThis, ipPrev, _TRUE, 16);
                    ipData->m_IPIndex--;
                    prevIndex--;
                    ipPrev = pIPTable[prevIndex];      /* Reset ipPrev to what is now preceding IP */
                }
            }
            else
            {
                swapPrev++;
                prevIndex--;
                ipPrev = pIPTable[prevIndex];  /* get (new) preceding IP */
            }
        }
#if DEBUG_IPT1A
        if (swapPrev > 0)
            Log(SWLogger_DEBUG, L" swapPrev (= %d) IP case!  Re-doing previous slope values...\n", swapPrev);
#endif
        if (prevIndex >= 1)
            ipPrev2 = pIPTable[prevIndex - 1];     /* get 2nd preceding IP */
        if ((swapPrev > 0) && (prevIndex < pThis->wIPCount - 1))
        {
            followIndex = prevIndex + 1;
            ipFollow = pIPTable[followIndex];  /* get following IP */
        }
        /* Set m_exitIndex field if we are inserting following a re-entry into keyboard */
        if (ipPrev->m_IPType == Entry)
        {
            if ((swapPrev == 0) && (pThis->lastExitIP != NULL))        /* Set m_exitIndex field if we just re-entered keyboard */
                ipData->m_exitIndex = pThis->lastExitIP->m_IPIndex;
            else
                ipData->m_exitIndex = ipPrev->m_exitIndex;
        }

        /* Check for any bogus Angle IPs that may have been added out-of-sequence after recognizing DoubleLetter gesture */
        if ((followIndex > 0) && (ipDouble != NULL))
        {
            AlwaysAssert(followIndex == pIPTable[followIndex]->m_IPIndex);
            checkIndex = followIndex;
            ipCheck = ipFollow;
            while ((checkIndex > 0) && (checkIndex < pThis->wIPCount) &&
                ((ipCheck->m_DoubleIPIndex == ipDouble->m_DoubleIPIndex) || SWCIPTable_includeAdjacentIP(ipDouble, ipCheck)))
            {
                AlwaysAssert(ipCheck == pIPTable[checkIndex]);   /* Make sure ipCheck set correctly */
                if (ipCheck->m_DoubleIPIndex != ipDouble->m_DoubleIPIndex)
                {
                    SWCIPTable_recordIncludedIP(pThis, ipDouble, ipCheck, 5);
#if DEBUG_IPT1A
                    if (ipCheck->m_IPType == PenUp)
                    {
                        Log(SWLogger_DEBUG, L" Including PenUp IP => (dIP = %d) @  [%d, %d] in DoubleLetter IP[%d]\n", ipCheck->m_DoubleIPIndex, ipCheck->m_IPLocation.x, ipCheck->m_IPLocation.y, ipDouble->m_DoubleIPIndex);
                    }
                    else
                    {
                        Log(SWLogger_DEBUG, L" Including type %d IP at [%d, %d] {%d} ** FOLLOWING 2 ** DoubleLetter IP[%d]\n", ipCheck->m_IPType, ipCheck->m_IPLocation.x, ipCheck->m_IPLocation.y, ipCheck->m_IPPosIndex, ipDouble->m_DoubleIPIndex);
                    }
                    SWCIPTable_dumpIPs(pThis, &pThis->m_aIPTable, &pThis->m_aIPTable2, 4, NULL);
#endif
                }
                checkIndex++;
                if (checkIndex < pThis->wIPCount)
                    ipCheck = pIPTable[checkIndex];        /* get (new) following IP */
                else
                {
                    checkIndex = 0;
                    ipCheck = NULL;
                }
            }
        }
    }

    switch (ipData->m_IPType)
    {
    case Exit:
        pThis->prevExitIP = pThis->lastExitIP;
        pThis->lastExitIP = ipData;
        ipData->m_IPFlags = EXIT_IP;
        ipData->m_fIPWeight = ExitWeight;
        ipData->m_fSkipPenalty = SKIPPED_EXIT_IP;
        if (pThis->m_ShiftLocCount < DEFAULT_WORD_SIZE)
        {
            if (ipData->m_IPLocation.x > 0)
                pThis->m_ExitXPos[pThis->m_ShiftLocCount] = ipData->m_IPLocation.x;
            else
                pThis->m_ExitXPos[pThis->m_ShiftLocCount] = 1;
            pThis->m_EntryXPos[pThis->m_ShiftLocCount] = 0;       /* Set next m_EntryXPos[] value to zero (invalid) until actual re-entry detected */
            pThis->m_EntryPosIndex[pThis->m_ShiftLocCount] = 0;   /* Set next m_EntryPosIndex[] value to zero (invalid) until actual re-entry detected */
            pThis->m_ShiftPos[pThis->m_ShiftLocCount++] = ipData->m_FixedIndexIn;
        }
        /* We may get exit-entry-exit sequence.  Check for preceding Entry IP, and if found, set ->m_exitIndex field. */
        if (prevIndex > 0)
        {
            if ((ipPrev->m_IPType == Entry) && (pThis->prevExitIP != NULL))
                ipData->m_exitIndex = pThis->prevExitIP->m_IPIndex;    /* Save corresponding Exit IP index in Entry IP's m_exitIndex field */
        }
        break;
    case Entry:
        if ((pThis->m_ShiftLocCount > 0) && (pThis->m_ShiftLocCount <= DEFAULT_WORD_SIZE))
        {
            if (ipData->m_IPLocation.x > 0)
                pThis->m_EntryXPos[pThis->m_ShiftLocCount - 1] = ipData->m_IPLocation.x;
            else
                pThis->m_EntryXPos[pThis->m_ShiftLocCount - 1] = 1;       /* Make sure that m_EntryXPos[] value is non-zero */
            pThis->m_EntryPosIndex[pThis->m_ShiftLocCount - 1] = ipData->m_FixedIndexOut;
        }
        ipData->m_IPFlags = 0;
        ipData->m_fIPWeight = EntryWeight;
        ipData->m_fSkipPenalty = SKIPPED_ENTRY_IP;
        ipData->m_exitIndex = pThis->lastExitIP->m_IPIndex;    /* Save corresponding Exit IP index in Entry IP's m_exitIndex field */
        break;
    case PathDivision:
        ipData->m_IPFlags = 0;
        ipData->m_fIPWeight = (float)0.0;
        ipData->m_fSkipPenalty = (float)0.0;
        break;
    case Tap:
        ipData->m_fIPWeight = TapWeight;
        ipData->m_fSkipPenalty = (float)0.0;
        break;
    case PenDown:
        pThis->firstLetterShiftGesture = _FALSE;
        ipData->m_IPFlags = (ANGLE_IP | REQUIRED_IP | MULTIPLE_IP);
        ipData->m_fIPWeight = PenDownWeight;
        ipData->m_fSkipPenalty = SKIPPED_PEN_DOWN;
        break;
    case Pause:
        ipData->m_IPFlags = (PAUSE_IP | MULTIPLE_IP);
        ipData->m_fIPWeight = PauseWeight;
        ipData->m_fSkipPenalty = SKIPPED_PAUSE_IP;
        break;
    case DoubleLetter:
        ipData->m_IPFlags = (ANGLE_IP | REQUIRED_IP);
        ipData->m_fIPWeight = DoubleLetterWeight;
        ipData->m_fSkipPenalty = SKIPPED_DOUBLE_IP;         /* Actual value set in GetSkippingPenalty()... */
        if (ipData->m_IPLocation.y < SWDbm_keyboardUpperBound(pDbm))
        {   /* Detect Double Letter IP path loop above keyboard, and set ShiftAll flag when detected */
            pThis->m_ShiftAll = _TRUE;
        }
        break;
    case PenUp:
        ipData->m_IPFlags = (ANGLE_IP | REQUIRED_IP | MULTIPLE_IP | OVERSHOOT_IP);
        ipData->m_fIPWeight = PenUpWeight;
        ipData->m_fSkipPenalty = SKIPPED_PEN_UP;
        if ((pThis->m_penUpDoubleIPIndex == 0) && (ipDouble != NULL) && (ipPrev != NULL))    /* Final check if we should Soften PenUp */
        {
            if ((ipPrev->m_DoubleIPIndex == ipDouble->m_DoubleIPIndex) &&
                (_SWPoint_distance(&ipData->m_IPLocation, &ipDouble->m_IPLocation) < DOUBLE_PENUP_INCLUDE_DISTANCE) &&
                SWCIPTable_PointsInSameRow(pThis, &ipData->m_IPLocation.y, ipDouble->m_IPLocation.y, 0, _FALSE) &&
                (ipData->m_FixedIndexIn <= (sw_max(ipDouble->m_FixedIndexOut, ipDouble->gestureOut) + DOUBLE_PENUP_INCLUDE_MARGIN)))
            {
                pThis->m_penUpDoubleIPIndex = ipDouble->m_DoubleIPIndex;
#if DEBUG_IPT1A || DEBUG_IPT1A1
                Log(SWLogger_DEBUG, L" PenUp @ [%d, %d] RE-CAST as SoftUp (CLOSE TO Double gesture)\n", ipData->m_IPLocation.x, ipData->m_IPLocation.y);
#endif
            }
        }
        if ((pThis->m_penUpDoubleIPIndex != 0) && (ipDouble != NULL) && (pThis->m_penUpDoubleIPIndex == ipDouble->m_DoubleIPIndex))
        {
            ipData->gestureLoc = ipDouble->m_IPLocation;
            ipData->gestureIn = ipDouble->m_FixedIndexIn;
            ipData->gestureOut = ipDouble->m_FixedIndexOut;
            ipData->m_IPType = SoftUp;              /* Treat as "soft" PenUp location... */
            /* Track number and location of included IPs */
            ipData->m_DoubleIPIndex = -pThis->m_penUpDoubleIPIndex;
            includeNewIPinDouble = _TRUE;
            if (ipDouble->signChangesOK)
            {
                ipData->m_IPFlags = 0;
                ipData->m_fIPWeight = SoftUpWeight;
                ipData->m_fSkipPenalty = SKIPPED_DOUBLE_IP_SOFT_UP;
#if DEBUG_IPT1A || DEBUG_IPT1A1
                Log(SWLogger_DEBUG, L" PenUp @ [%d, %d] RE-CAST as SoftUp (Double gesture)\n", ipData->m_IPLocation.x, ipData->m_IPLocation.y);
#endif
            }
            else
            {
                ipData->m_fIPWeight *= (float)0.67;
                ipData->m_fSkipPenalty *= (float)0.67;
#if DEBUG_IPT1A || DEBUG_IPT1A1
                Log(SWLogger_DEBUG, L" PenUp @ [%d, %d] RE-CAST as Softened-Up (Double gesture)\n", ipData->m_IPLocation.x, ipData->m_IPLocation.y);
#endif
            }
        }
        else
        {
#if DEBUG_IPT1A || DEBUG_IPT1A1
            if (pThis->m_penUpDoubleIPIndex != 0)
            {
                if (ipDouble == NULL)
                    Log(SWLogger_DEBUG, L" m_penUpDoubleIPIndex = %d but no DoubleIPs detected!\n", pThis->m_penUpDoubleIPIndex);
                if ((ipDouble != NULL) && (pThis->m_penUpDoubleIPIndex != ipDouble->m_DoubleIPIndex))
                    Log(SWLogger_DEBUG, L"m_penUpDoubleIPIndex = %d but ipDouble->m_DoubleIPIndex = %d!\n", pThis->m_penUpDoubleIPIndex, ipDouble->m_DoubleIPIndex);
            }

#endif
            pThis->m_penUpDoubleIPIndex = 0;
        }
        break;
    case PauseAngle:
    case Angle:
        {
            if (ipData->m_IPType == PauseAngle)
            {
                ipData->m_IPFlags = (PAUSE_IP | ANGLE_IP | MULTIPLE_IP);
            }
            else
            {
                /*ipData->m_IPFlags = (ANGLE_IP | REQUIRED_IP | MULTIPLE_IP | OVERSHOOT_IP); */
                ipData->m_IPFlags = (ANGLE_IP | REQUIRED_IP | MULTIPLE_IP);
            }
            /* SavedCodeSegments/SearchDB.cpp/105 */
            {
                float AngleWeightRatio = (float)(SWCIPAnalyzer_GetFixedD2Sum(pThis->m_backend->pIPAnalyzer, ipData->m_FixedIndexIn) - DIF2_ANGLE_THRESHOLD) / (float)(DIF2_MAX_VALUE - DIF2_ANGLE_THRESHOLD);
                ipData->m_fIPWeight = AngleWeight + (AngleWeightRatio * (AngleWeightMax - AngleWeight));
                if (ipData->m_IPType == PauseAngle)
                {
                    ipData->m_fIPWeight *= PAUSE_ANGLE_WEIGHT_FACTOR;       /* Reduce weighting if stylus motion paused at IP location */
                    anglePenaltyAdjust = ipData->m_fIPWeight - PauseAngleWeight;
                    anglePenaltyAdjust = (anglePenaltyAdjust / (PauseAngleWeightMax - PauseAngleWeight)) * (SKIPPED_PAUSEANGLE_IP_MAX - SKIPPED_PAUSEANGLE_IP_MIN);
                    ipData->m_fSkipPenalty = SKIPPED_ANGLE_IP_MIN + anglePenaltyAdjust;
                }
                else
                {
                    anglePenaltyAdjust = ipData->m_fIPWeight - AngleWeight;
                    anglePenaltyAdjust = (anglePenaltyAdjust / (AngleWeightMax - AngleWeight)) * (SKIPPED_ANGLE_IP_MAX - SKIPPED_ANGLE_IP_MIN);
                    ipData->m_fSkipPenalty = SKIPPED_ANGLE_IP_MIN + anglePenaltyAdjust;
                }
                break;
            }
        }

    case Multiple:
        ipData->m_IPFlags = (PAUSE_IP | MULTIPLE_IP);
        ipData->m_fIPWeight = MultipleWeight;
        ipData->m_fSkipPenalty = SKIPPED_MULTIPLE_IP;
        break;

    case SoftDown:
    case SoftUp:
    case TapHold:
    case MaxIPType:
        break;
    }

    /* Set length ratio for entire path in PenDown IP so that it can be accessed when needed (e.g. in ProcessPath()) */
    if (ipData->m_IPType == PenUp)
    {
        _SWPoint p1, p2;
        SWCIPTableRow *ipPenDown;
        p1 = SWCIPAnalyzer_GetFixedPoint(pThis->m_backend->pIPAnalyzer, 0, 23);
        p2 = SWCIPAnalyzer_GetFixedPoint(pThis->m_backend->pIPAnalyzer, ipData->m_FixedIndexIn, 25);
        ipPenDown = pIPTable[0];
        ipPenDown->m_fLengthRatio = (float)1.0;         /* Init to default value */
        {
            SBYTE2 penDownToPenUpDistance = _SWPoint_distance8(&p1, &p2);
            SBYTE2 penDownToPenUpPathLen = SWCIPAnalyzer_GetPathLength8(pThis->m_backend->pIPAnalyzer, 0, ipData->m_FixedIndexIn);
            if ((penDownToPenUpPathLen > penDownToPenUpDistance) && (penDownToPenUpDistance > 0))
            {
                ipPenDown->m_fLengthRatio = (float)penDownToPenUpPathLen / (float)penDownToPenUpDistance;
            }
        }
    }

    /* Make sure bottomRow flag is initialized (especially for PenDown case, where code to set bottomRow is not executed) */
    ipData->bottomRow = _FALSE;
#if ENABLE_DIACRITIC_GESTURES
    /* If IP is outside of Diacritic Gesture boundary, then flag it as such */
    if (ipData->m_IPLocation.y > (pThis->m_backend->sScreenGeometry.keyboardHeight + DIACRITIC_GESTURE_MARGIN))
        ipData->m_IPFlags |= DIACRITIC_IP;
#endif
    /* SavedCodeSegments/SearchDB.cpp/106 */

    /* Nothing to do or check for PenDown or Tap IPs... */
    if (ipPrev != NULL)
    {
        /* Check for "Shift" (or "Tap-Shift") gesture */
        if ( ((ipData->m_IPLocation.y < SHIFT_GESTURE_MARGIN) && (ipData->m_IPType < Exit)) ||      /* Exclude Exit, Entry, PathDivision, Tap */
            /* Also check for any case where where an IP is created during a Shift Gesture... */
             ((ipPrev->m_IPType == Exit) && ((ipData->m_IPType < Exit) || (ipData->m_IPType == PathDivision))) )
        {
            ET9BOOL deleteCurrentIP = _FALSE;
            if ((ipData->m_IPType == PenUp) && (ipPrev->m_IPType == Exit))
            {
#if DETECT_TAP_SHIFT_GESTURES
                if ((pThis->wIPCount == 2) && SWCIPAnalyzer_IsCheckTap(pThis->m_backend->pIPAnalyzer))
                {
                    ipCheck = pIPTable[0];   /* get PenDown IP */
                    if ((ipCheck->m_IPType == PenDown)
#if !ENABLE_TAP_SHIFT_GESTURES
                        && (ipCheck->m_IPLocation.y <= KEYBOARD_UPPER_BOUND)
#endif
                        )
                    {
                        ipCheck->m_IPType = Tap;
                        ipCheck->m_IPFlags = TAP_IP;
#if ENABLE_TAP_SHIFT_GESTURES
                        SWStateMachine::ForceShiftOn();
#endif
                        SWCIPTable_ProcessIPTableRow(pThis, 0);
                        deleteCurrentIP = _TRUE;
                    }
                }
#endif
                if ((pThis->wIPCount >= 2) && !deleteCurrentIP)   /* Re-cast previous Exit IP as PenUp location */
                {
                    SBYTE2 prevIPIndex;
                    SBYTE2 i;
                    if (SWCIPAnalyzer_IsCheckTap(pThis->m_backend->pIPAnalyzer) || SWCIPAnalyzer_IsCheckTapHold(pThis->m_backend->pIPAnalyzer))
                    {
                        /* Don't signal Z1 input if this can still be a key-rubbing gesture */
                        SWCIPTable_CheckSignalDetectZ1Input(pThis);
                    }
                    prevIPIndex = ipPrev->m_IPIndex;
                    for (i = pThis->wIPCount - 1; i > prevIPIndex; i--)
                    {
                        /* Delete IPs following Exit IP re-cast as new PenUp */
#if DEBUG_IPT1A
                        Log(SWLogger_DEBUG, L" Deleting type %d IP @ [%d, %d] preceding Exit IP\n", ipCheck->m_IPType, ipCheck->m_IPLocation.x, ipCheck->m_IPLocation.y);
#endif
                        ipCheck = pIPTable[i];
                        SWCIPTable_deleteIP(pThis, ipCheck, _TRUE, 1);
                    }
                    ipPrev->m_IPType = SoftUp;              /* Treat as "soft" PenUp location... */
                    ipPrev->m_IPFlags = 0;
                    ipPrev->m_fIPWeight = ExitUpWeight;
                    ipPrev->m_fSkipPenalty = SKIPPED_SOFT_UP;
                    ipPrev->m_FixedIndexOut = ipPrev->m_FixedIndexIn;
                    ipPrev->minX = ipData->minX;
                    ipPrev->maxX = ipData->maxX;
                    ipPrev->minY = 0;               /* Ignore excursion above top of keyboard for pathSize calculation */
                    ipPrev->maxY = ipData->maxY;
#if DEBUG_IPT1A
                    Log(SWLogger_DEBUG, L" Exit -> PenUp. ipPrev->m_RowFilled = %d\n", ipPrev->m_RowFilled);
#endif
                    ipPrev->m_RowFilled = (ipPrev->m_RowFilled & INITIAL_DISTANCES);
                    SWCIPTable_ProcessIPTableRow(pThis, prevIPIndex);
                    deleteCurrentIP = _TRUE;
                }
            }
            /* Check for CAP-LOCK gesture: DoubleIP performed above keyboard */
            else if (ipData->m_IPType == DoubleLetter)
            {
                pThis->m_ShiftAll = _TRUE;
            }
#if DEBUG_IPT1A
            Log(SWLogger_DEBUG, L" Deleting type %d IP @ [%d, %d] above keyboard\n", ipData->m_IPType, ipData->m_IPLocation.x, ipData->m_IPLocation.y);
#endif
            if (deleteCurrentIP || (ipData->m_IPType != PenUp))
            {
                SWCIPTable_deleteIP(pThis, ipData, _FALSE, 2);  /* Don't add IP outside of top of keyboard. Delete now since not added to table */
#if DEBUG_IPT1A
                SWCIPTable_dumpIPs(pThis, &pThis->m_aIPTable, &pThis->m_aIPTable2, 5, NULL);
#endif
                return IPT_FAILURE;
            }
        }
        AlwaysAssert(ipData->m_IPType != DoubleLetter);

        ipDataInDouble = (ET9BOOL)(ipData->m_DoubleIPIndex != 0);
        ipPrevInDouble = (ET9BOOL)(ipPrev->m_DoubleIPIndex != 0);
        if (ipFollow != NULL)
            ipFollowInDouble = (ET9BOOL)(ipFollow->m_DoubleIPIndex != 0);

#if DETECT_TAP_CONTROL_GESTURES
        /* Check for "Control-Tap" gesture.  Must extend at least CTRL_GESTURE_MARGIN pixels below bottom of keyboard, and */
        /*   differ from a vertical line by no more than CTRL_GESTURE_SLOPE_THRESHOLD.  Also, allow user to "bounce off" bottom */
        /*   of display by checking for an Angle IP below the keyboard, where stylus is lifted prior to re-entering keyboard area. */
        if ((ipData->m_IPLocation.y > pThis->m_backend->sScreenGeometry.keyboardHeight) && (ipData->m_IPType == PenUp))
        {
            if ( ( (SWIPTableRowArray_size(&pThis->m_aIPTable) == 1) && (ipPrev->m_IPType == PenDown) && (ipData->m_IPLocation.y > (pThis->m_backend->sScreenGeometry.keyboardHeight + CTRL_GESTURE_MARGIN)) ) ||
                 ( (SWIPTableRowArray_size(&pThis->m_aIPTable) == 2) && (ipPrev->m_IPType == Angle)   && (ipPrev->m_IPLocation.y > (pThis->m_backend->sScreenGeometry.keyboardHeight + CTRL_GESTURE_MARGIN)) ) )
            {
                ipCheck = pIPTable[0];   /* get PenDown IP */

                _SWVector    pathVector;
                if (SWIPTableRowArray_size(&pThis->m_aIPTable) == 1)
                    pathVector = _SWVector(ipCheck->m_IPLocation, ipData->m_IPLocation);
                else
                    pathVector = _SWVector(ipCheck->m_IPLocation, ipPrev->m_IPLocation);
                if (pathVector.verticalSlopeDifference() < CTRL_GESTURE_SLOPE_THRESHOLD)        /* ??? Require threshold? Reduce threshold? */
                {
                    ipCheck->m_IPType = Tap;
                    ipCheck->m_IPFlags = TAP_IP;
#if ENABLE_TAP_CONTROL_GESTURES
                    SWStateMachine::ForceCtrlOn();
#endif
                    SWCIPTable_ProcessIPTableRow(pThis, 0);
#if DEBUG_IPT1A
                    Log(SWLogger_DEBUG, L" Deleting type %d IP @ [%d, %d] below keyboard\n", ipData->m_IPType, ipData->m_IPLocation.x, ipData->m_IPLocation.y);
#endif
                    SWCIPTable_deleteIP(pThis, ipData, _FALSE, 3);       /* Don't add IP below keyboard. Delete now since not added to table */
#if DEBUG_IPT1A
                    SWCIPTable_dumpIPs(pThis, &pThis->m_aIPTable, &pThis->m_aIPTable2, 6, NULL);
#endif
                    return IPT_FAILURE;
                }
            }
        }
#endif
        if ((SWCIPAnalyzer_IsCheckTap(pThis->m_backend->pIPAnalyzer) || SWCIPAnalyzer_IsCheckTapHold(pThis->m_backend->pIPAnalyzer)) &&
            (ipData->m_IPType != Exit) && (ipData->m_IPType != Pause) && (ipData->m_IPType != PenUp))
        {
            /* Don't signal Z1 input if this can still be a key-rubbing gesture */
            SWCIPTable_CheckSignalDetectZ1Input(pThis);
        }

        /* Check for preceding bogus "Key-Hopping" IP */
        if ((ipPrev2 != NULL) && (ipPrev->m_IPLocation.y < KEYBOARD_UPPER_BOUND) && (ipPrev->m_IPLocation.y > KEYBOARD_UPPER_NEIGHBORHOOD) && (ipPrev->m_IPType == Angle))
        {
            if ((ipPrev2->m_IPLocation.y < TOP_ROW_LOWER_BOUND) && (ipPrev2->m_IPLocation.y >= KEYBOARD_UPPER_BOUND) &&
                (ipData->m_IPLocation.y < TOP_ROW_LOWER_BOUND) && (ipData->m_IPLocation.y >= KEYBOARD_UPPER_BOUND))
            {
                AlwaysAssert(ipPrev == pIPTable[prevIndex]); /* Make sure ipPrev set correctly for deletion */
#if DEBUG_IPT1A
                Log(SWLogger_DEBUG, L" Deleting type %d IP at [%d, %d] in Top-Row.\n", ipPrev->m_IPType, ipPrev->m_IPLocation.x, ipPrev->m_IPLocation.y);
#endif
                SWCIPTable_deleteIP(pThis, ipPrev, _TRUE, 4);
                ipData->m_IPIndex--;
                prevIndex--;
                ipPrev = ipPrev2;       /* Reset ipPrev to what is now preceding IP */
                if (ipPrev->m_IPType == Entry)
                    ipData->m_exitIndex = ipPrev->m_exitIndex;
                if (ipData->m_IPIndex > 1)
                    ipPrev2 = pIPTable[ipData->m_IPIndex - 2];
                else
                    ipPrev2 = NULL;
                if (followIndex > 0)
                {
                    followIndex--;
                    AlwaysAssert(followIndex == pIPTable[followIndex]->m_IPIndex);
                }
            }
        }
        /* SavedCodeSegments/SearchDB.cpp/96 */
        /* Check for a bogus Pause IP that may have been added just after PenDown */
        if (!ipDataInDouble && (ipData->m_IPType == Pause) && (ipPrev->m_IPType == PenDown))
        {
            if (((ipData->m_FixedIndexIn - ipPrev->m_FixedIndexOut) <= PENDOWN_PAUSE_MIN_INTERVALS) &&
                (_SWPoint_distance(&ipPrev->m_IPLocation, &ipData->m_IPLocation) <= PENDOWN_PAUSE_MIN_DISTANCE))
            {
#if DEBUG_IPT1A
                Log(SWLogger_DEBUG, L" Deleting Pause IP @ [%d, %d] following PenDown\n", ipData->m_IPLocation.x, ipData->m_IPLocation.y);
#endif
                SWCIPTable_deleteIP(pThis, ipData, _FALSE, 5);       /* Hasn't been added to Ptr array yet, so no erase() call needed */
#if DEBUG_IPT1A
                SWCIPTable_dumpIPs(pThis, &pThis->m_aIPTable, &pThis->m_aIPTable2, 7, NULL);
#endif
                return IPT_FAILURE;
            }
        }
        /* Check for a bogus Angle IP that may have been added just after PenDown */
        if (!ipDataInDouble && (ipData->m_IPType == Angle) && (ipPrev->m_IPType == PenDown))
        {
            if (((ipData->m_FixedIndexIn - ipPrev->m_FixedIndexOut) <= PENDOWN_ANGLE_MIN_INTERVALS) &&
                (_SWPoint_distance(&ipPrev->m_IPLocation, &ipData->m_IPLocation) <= PENDOWN_ANGLE_MIN_DISTANCE))
            {
                if (pThis->m_backend->m_pSearchDB->algConvertAngleCloseToPenDown[ALG_MGD])
                {
#if DEBUG_IPT1D
                    Log(SWLogger_DEBUG, L" Angle IP @ [%d, %d] NEAR PenDown @ [%d, %d] CONVERTED to PauseAngle IP\n", ipData->m_IPLocation.x, ipData->m_IPLocation.y, ipPrev->m_IPLocation.x, ipPrev->m_IPLocation.y);
                    SWCIPTable_dumpIPs(pThis, &pThis->m_aIPTable, &pThis->m_aIPTable2, 48, NULL);
#endif
                    SWCIPTable_convertIPtoPauseAngle(ipData, PAUSE_ANGLE_WEIGHT_FACTOR);
                }
                else
                {
                    BYTE4 timeDif;
                    BYTE2 mergedX = ((ipPrev->m_IPLocation.x * ipPrev->m_MergeCount) + ipData->m_IPLocation.x + ipPrev->m_MergeCount) / (ipPrev->m_MergeCount + 1);
                    BYTE2 mergedY = ((ipPrev->m_IPLocation.y * ipPrev->m_MergeCount) + ipData->m_IPLocation.y + ipPrev->m_MergeCount) / (ipPrev->m_MergeCount + 1);
#if DEBUG_IPT1D
                    Log(SWLogger_DEBUG, L" Angle @ [%d, %d] MERGED with PenDown\n", ipData->m_IPLocation.x, ipData->m_IPLocation.y);
#endif
                    ipPrev->m_IPLocation.x = mergedX;
                    ipPrev->m_IPLocation.y = mergedY;
                    timeDif = (ipData->m_TimeOffset - ipPrev->m_TimeOffset) / (ipPrev->m_MergeCount + 1);
                    ipPrev->m_TimeOffset += timeDif;
                    ipPrev->m_MergeCount++;
                    ipPrev->m_FixedIndexOut = ipData->m_FixedIndexOut;
                    ipPrev->m_RowFilled = CALCULATE_ROW;
                    ipPrev->m_IPPosIndex = (ipPrev->m_FixedIndexIn + ipPrev->m_FixedIndexOut) / 2;
                    SWCIPTable_deleteIP(pThis, ipData, _FALSE, 6);       /* Hasn't been added to Ptr array yet, so no erase() call needed */
                    SWCIPTable_DoProcessTableRows(pThis);
#if DEBUG_IPT1D
                    SWCIPTable_dumpIPs(pThis, &pThis->m_aIPTable, &pThis->m_aIPTable2, 8, NULL);
#endif
                    return IPT_FAILURE;
                }
            }
        }
        /* Check for a bogus Angle (or PauseAngle) IP that may have been added just before PenUp */
        /* Also check for a bogus Pause IP that may have been added just before PenUp */
        /* If found, user may have just jiggled stylus/finger when lifting.   */
        /* Preserve previous Angle IP if it is already a merged IP or from a DoubleLetter gesture,  */
        /* or if it is possibly intended as part of a scribble gesture (based on preceding segment length),    */
        /* but convert the preceding IP to the PenUp location assuming the final short segment is just unintended jitter in the path.  */
        /* (SavedCodeSegments/231: Treat previous Angle as actual PenUp location.)   */
        if ((ipData->m_IPType == PenUp) && ((ipPrev->m_IPType == Angle) || (ipPrev->m_IPType == PauseAngle) || (ipPrev->m_IPType == Pause)))
        {
        #if !CONVERT_PENUP_TO_SOFTUP_NEAR_PRECEDING_IP
            ET9BOOL baseRequirements = ((!ipPrevInDouble && !ipDataInDouble) || (ipPrev->m_IPType == Pause));
        #else
            /*ET9BOOL baseRequirements = ((ipPrev->m_MergeCount == 1) && !ipPrevInDouble && !ipDataInDouble); */
            ET9BOOL baseRequirements = (!ipPrevInDouble && !ipDataInDouble);
        #endif
            SBYTE2  pathIntervals = ipData->m_FixedIndexIn - ipPrev->m_FixedIndexOut;
            BYTE2   pathDistance = _SWPoint_distance(&ipPrev->m_IPLocation, &ipData->m_IPLocation);

#if !CONVERT_PENUP_TO_SOFTUP_NEAR_PRECEDING_IP
            /* Convert IP preceding PenUp to PenUp when criteria are met */
            if (baseRequirements && (pathIntervals <= PENUP_PAUSE_OR_ANGLE_MIN_INTERVALS) && (pathDistance <= PENUP_PAUSE_OR_ANGLE_MIN_DISTANCE))
            {
#if DEBUG_IPT1A || DEBUG_IPT1A1
                Log(SWLogger_DEBUG, L" IP of type %d @ [%d, %d] RE-CAST as PenUp! \n", ipPrev->m_IPType, ipPrev->m_IPLocation.x, ipPrev->m_IPLocation.y);
#endif
                ipPrev->m_IPType = PenUp;
                ipPrev->m_IPFlags = (ANGLE_IP | REQUIRED_IP | MULTIPLE_IP | OVERSHOOT_IP);
                ipPrev->m_fIPWeight = PenUpWeight;
                ipPrev->m_fSkipPenalty = SKIPPED_PEN_UP;
                ipPrev->minX = ipData->minX;
                ipPrev->maxX = ipData->maxX;
                ipPrev->minY = ipData->minY;
                ipPrev->maxY = ipData->maxY;

                ipPrev->m_RowFilled = CALCULATE_ROW;
/*// */
                if ((pThis->m_penUpDoubleIPIndex == 0) && (ipDouble != NULL))    /* Repeat final check if we should Soften PenUp */
                {
                    if ((ipPrev->m_DoubleIPIndex == ipDouble->m_DoubleIPIndex) &&
                        (_SWPoint_distance(&ipPrev->m_IPLocation, &ipDouble->m_IPLocation) < DOUBLE_PENUP_INCLUDE_DISTANCE) &&
                        SWCIPTable_PointsInSameRow(pThis, ipPrev->m_IPLocation.y, ipDouble->m_IPLocation.y, 0, _FALSE) &&
                        (ipPrev->m_FixedIndexIn <= (sw_max(ipDouble->m_FixedIndexOut, ipDouble->gestureOut) + DOUBLE_PENUP_INCLUDE_MARGIN)))
                    {
                        pThis->m_penUpDoubleIPIndex = ipDouble->m_DoubleIPIndex;
                    }
                }
                if ((pThis->m_penUpDoubleIPIndex != 0) && (ipDouble != NULL) && (pThis->m_penUpDoubleIPIndex == ipDouble->m_DoubleIPIndex))
                {
                    ipPrev->gestureLoc = ipDouble->m_IPLocation;
                    ipPrev->gestureIn = ipDouble->m_FixedIndexIn;
                    ipPrev->gestureOut = ipDouble->m_FixedIndexOut;
                    ipPrev->m_IPType = SoftUp;              /* Treat as "soft" PenUp location... */
#if DEBUG_IPT1A || DEBUG_IPT1A1
                    Log(SWLogger_DEBUG, L" Angle/PenUp @ [%d, %d] RE-CAST as SoftUp (INCLUDED IN Double gesture)\n", ipPrev->m_IPLocation.x, ipPrev->m_IPLocation.y);
#endif
                    /* Track number and location of included IPs (check whether already included) */
                    if (ipPrev->m_DoubleIPIndex != pThis->m_penUpDoubleIPIndex)
                    {
                        ipPrev->m_DoubleIPIndex = -pThis->m_penUpDoubleIPIndex;
                        includeNewIPinDouble = _TRUE;
                    }
                    if (ipDouble->signChangesOK)
                    {
                        ipPrev->m_IPFlags = 0;
                        ipPrev->m_fIPWeight = SoftUpWeight;
                        ipPrev->m_fSkipPenalty = SKIPPED_DOUBLE_IP_SOFT_UP;
#if DEBUG_IPT1A || DEBUG_IPT1A1
                        Log(SWLogger_DEBUG, L" PenUp @ [%d, %d] RE-CAST as SoftUp (Double gesture) (2)\n", ipData->m_IPLocation.x, ipData->m_IPLocation.y);
#endif
                    }
                    else
                    {
                        ipPrev->m_fIPWeight *= (float)0.67;
                        ipPrev->m_fSkipPenalty *= (float)0.67;
#if DEBUG_IPT1A || DEBUG_IPT1A1
                        Log(SWLogger_DEBUG, L" PenUp @ [%d, %d] RE-CAST as Softened-Up (Double gesture) (2)\n", ipData->m_IPLocation.x, ipData->m_IPLocation.y);
#endif
                    }
                }
                else
                {
#if DEBUG_IPT1A1
                    if (pThis->m_penUpDoubleIPIndex != 0)
                    {
                        if (ipDouble == NULL)
                            Log(SWLogger_DEBUG, L" m_penUpDoubleIPIndex = %d but no DoubleIPs detected (2)!\n", pThis->m_penUpDoubleIPIndex);
                        if ((ipDouble != NULL) && (pThis->m_penUpDoubleIPIndex != ipDouble->m_DoubleIPIndex))
                            Log(SWLogger_DEBUG, L"m_penUpDoubleIPIndex = %d but ipDouble->m_DoubleIPIndex = %d (2)!\n", pThis->m_penUpDoubleIPIndex, ipDouble->m_DoubleIPIndex);
                    }

#endif
                    pThis->m_penUpDoubleIPIndex = 0;
                }
/*// */

                SWCIPTable_deleteIP(pThis, ipData, _FALSE, 7);       /* Hasn't been added to Ptr array yet, so no erase() call needed */
                SWCIPTable_DoProcessTableRows(pThis);
#if DEBUG_IPT1A1
                Log(SWLogger_DEBUG, L" Angle @ [%d, %d] RE-CAST as PenUp\n", ipPrev->m_IPLocation.x, ipPrev->m_IPLocation.y);
                SWCIPTable_dumpIPs(pThis, &pThis->m_aIPTable, &pThis->m_aIPTable2, 9, NULL);
#endif
                return IPT_FAILURE;
            }
            /* Convert Angle IP preceding PenUp to PauseAngle when secondary criteria are met */
            else if ((ipPrev->m_IPType == Angle) && (pathIntervals <= PENUP_ANGLE_MIN_INTERVALS_2) && (pathDistance <= PENUP_ANGLE_MIN_DISTANCE_2))
            {
                SWCIPTable_convertIPtoPauseAngle(ipPrev, PAUSE_ANGLE_WEIGHT_FACTOR_2);
#if DEBUG_IPT1A || DEBUG_IPT1A1
                Log(SWLogger_DEBUG, L"Angle @ [%d, %d] RE-CAST as PauseAngle\n", ipPrev->m_IPLocation.x, ipPrev->m_IPLocation.y);
                SWCIPTable_dumpIPs(pThis, &pThis->m_aIPTable, &pThis->m_aIPTable2, 29, NULL);
#endif
            }
#else  /* #if  CONVERT_PENUP_TO_SOFTUP_NEAR_PRECEDING_IP */
            /* If criteria to eliminate PenUp were not met, see if PenUp should be re-cast as a SoftUp (or PATHCHANGE IP deleted) */
            if (baseRequirements && (pathIntervals <= SOFTUP_ANGLE_MIN_INTERVALS) && (pathDistance <= SOFTUP_ANGLE_MIN_DISTANCE))
            {
                float weightRatio;
                ipData->m_IPType = SoftUp;              /* Treat as "soft" PenUp location... */
                ipData->m_IPFlags = (ANGLE_IP | MULTIPLE_IP);       /* Don't treat as required, and don't allow overshoot */
                /*ipData->m_fIPWeight *= (float)0.67; */
                /*ipData->m_fSkipPenalty *= (float)0.67; */
                weightRatio = (float)sw_max(pathDistance, 1) / (float)SOFTUP_ANGLE_MIN_DISTANCE;
                ipData->m_fIPWeight *= (weightRatio * (float)0.5);
                ipData->m_fSkipPenalty *= (weightRatio * (float)0.2);
#if DEBUG_IPT1A || DEBUG_IPT1A1
                Log(SWLogger_DEBUG, L" PenUp @ [%d, %d] RE-CAST as Softened-Up (Wt: %.2f SP: %.2f\n", ipData->m_IPLocation.x, ipData->m_IPLocation.y, ipData->m_fIPWeight, ipData->m_fSkipPenalty);
                SWCIPTable_dumpIPs(pThis, &pThis->m_aIPTable, &pThis->m_aIPTable2, 10, NULL);
#endif
            }
#endif
        }
            /* SavedCodeSegments/SearchDB.cpp/110 // Check for a bogus Angle IP that may have been added just after another Angle IP */
        /* Check for Pause near Angle IP (or PauseAngle IP) and convert to PauseAngle if found */
        if ( ( ((ipData->m_IPType == Angle) || (ipData->m_IPType == PauseAngle)) && (ipPrev->m_IPType == Pause) ) ||
                  ( (ipData->m_IPType == Pause) && ((ipPrev->m_IPType == Angle) || (ipPrev->m_IPType == PauseAngle)) ) )
        {
            if (!ipPrevInDouble && !ipDataInDouble && (_SWPoint_distance(&ipPrev->m_IPLocation, &ipData->m_IPLocation) < SOFTENING_DISTANCE))
            {
                if (ipPrev->m_IPType == Pause)
                {
                    if (ipData->m_IPType == Angle)      /* Don't adjust again if already merged with another Pause */
                    {
                        SWCIPTable_convertIPtoPauseAngle(ipData, PAUSE_ANGLE_WEIGHT_FACTOR);
                    }
                    ipData->m_IPFlags = (PAUSE_IP | ANGLE_IP | MULTIPLE_IP);
                    if (prevIndex > 0)      /* Sanity check */
                    {
                        AlwaysAssert(ipPrev == pIPTable[prevIndex]); /* Make sure ipPrev set correctly for deletion */
#if DEBUG_IPT1A
                        Log(SWLogger_DEBUG, L" Deleting Pause IP @ [%d, %d], convert Angle to PauseAngle\n", ipPrev->m_IPType, ipPrev->m_IPLocation.x, ipPrev->m_IPLocation.y);
#endif
                        SWCIPTable_deleteIP(pThis, ipPrev, _TRUE, 9);
                        ipData->m_IPIndex--;
                        prevIndex--;            /* Reset ipPrev to what is now preceding IP */
                        ipPrev = pIPTable[prevIndex];  /* get (new) preceding IP */
                        if (ipPrev->m_IPType == Entry)
                            ipData->m_exitIndex = ipPrev->m_exitIndex;
                        if (ipData->m_IPIndex > 1)
                            ipPrev2 = pIPTable[ipData->m_IPIndex - 2];
                        else
                            ipPrev2 = NULL;
                        if (followIndex > 0)
                        {
                            followIndex--;
                            AlwaysAssert(followIndex == pIPTable[followIndex]->m_IPIndex);
                        }
                    }
                }
                else
                {
#if DEBUG_IPT1A
                    Log(SWLogger_DEBUG, L" Deleting Pause IP @ [%d, %d], convert Angle to PauseAngle\n", ipData->m_IPLocation.x, ipData->m_IPLocation.y);
#endif
                    if (ipPrev->m_IPType == Angle)      /* Don't adjust again if already merged with another Pause */
                    {
                        SWCIPTable_convertIPtoPauseAngle(ipPrev, PAUSE_ANGLE_WEIGHT_FACTOR);
                    }
                    ipPrev->m_IPFlags = (PAUSE_IP | ANGLE_IP | MULTIPLE_IP);
                    SWCIPTable_deleteIP(pThis, ipData, _FALSE, 10);      /* Hasn't been added to Ptr array yet, so no erase() call needed */
#if DEBUG_IPT1A
                    SWCIPTable_dumpIPs(pThis, &pThis->m_aIPTable, &pThis->m_aIPTable2, 11, NULL);
#endif
                    return IPT_FAILURE;
                }
            }
        }
        /* Check for any bogus Angle IPs or other IPs that may be added */
        /*   just after re-entry from a Shift gesture */
        if (ipPrev->m_IPType == Entry)
        {
            if ( (ipData->m_IPType == Angle) || (ipData->m_IPType == Pause) ||
                 (ipData->m_IPType == PauseAngle) )
            {
                if (!ipDataInDouble && (ipData->m_IPLocation.y < ENTRY_IP_MIN_Y_INTERVAL))
                {
#if DEBUG_IPT1A
                    SWCIPTable_dumpIPType(pThis, ipData);
                    Log(SWLogger_DEBUG, L"IP at [%d, %d] deleted at ***RE-ENTRY***\n", ipData->m_IPType, ipData->m_IPLocation.x, ipData->m_IPLocation.y);
#endif
                    SWCIPTable_deleteIP(pThis, ipData, _FALSE, 11);      /* Hasn't been added to Ptr array yet, so no erase() call needed */
#if DEBUG_IPT1A
                    SWCIPTable_dumpIPs(pThis, &pThis->m_aIPTable, &pThis->m_aIPTable2, 12, NULL);
#endif
                    return IPT_FAILURE;
                }
            }
        }
        if (!ipDataInDouble && !ipPrevInDouble)
        {
            /* Check for a bogus Angle IP that may have been added just before or after another Angle IP */
            if (((ipPrev->m_IPType == Angle) || (ipPrev->m_IPType == PauseAngle)) && ((ipData->m_IPType == Angle) || (ipData->m_IPType == PauseAngle)))
            {
                ET9BOOL horizontalNeighbors = (ET9BOOL)(abs(ipData->m_IPLocation.x - ipPrev->m_IPLocation.x) <= MAX_HORIZONTAL_NEIGHBOR_DISTANCE);
                ET9BOOL verticalNeighbors = (ET9BOOL)(abs(ipData->m_IPLocation.y - ipPrev->m_IPLocation.y) <= MAX_VERTICAL_NEIGHBOR_DISTANCE);
                ET9BOOL nearNeighbors = (ET9BOOL)((ipData->m_IPPosIndex - ipPrev->m_IPPosIndex) <= MAX_NEIGHBOR_INTERVALS);
                if (horizontalNeighbors && verticalNeighbors && nearNeighbors)
                {
                    ET9BOOL keepPrevAngle = (ET9BOOL)(ipPrev->m_fIPWeight > ipData->m_fIPWeight);
                    if (pThis->m_backend->m_pSearchDB->algConvertAngleCloseToAngle[ALG_MGD])
                    {
                        if ((ipPrev->m_IPType == Angle) && !keepPrevAngle)
                        {
#if DEBUG_IPT1D
                            Log(SWLogger_DEBUG, L" Angle IP @ [%d, %d] {%d} NEAR IP @ [%d, %d] {%d} CONVERTED to PauseAngle IP\n", ipPrev->m_IPLocation.x, ipPrev->m_IPLocation.y, ipPrev->m_IPPosIndex, ipData->m_IPLocation.x, ipData->m_IPLocation.y, ipData->m_IPPosIndex);
#if DEBUG_IPT1A
                            SWCIPTable_dumpIPs(pThis, &pThis->m_aIPTable, &pThis->m_aIPTable2, 41, NULL);
#endif
#endif
                            SWCIPTable_convertIPtoPauseAngle(ipPrev, PAUSE_ANGLE_WEIGHT_FACTOR);
                        }
                        if ((ipData->m_IPType == Angle) && keepPrevAngle)
                        {
#if DEBUG_IPT1D
                            Log(SWLogger_DEBUG, L" Angle IP @ [%d, %d] {%d} NEAR IP @ [%d, %d] {%d} CONVERTED to PauseAngle IP\n", ipData->m_IPLocation.x, ipData->m_IPLocation.y, ipData->m_IPPosIndex, ipPrev->m_IPLocation.x, ipPrev->m_IPLocation.y, ipPrev->m_IPPosIndex);
#if DEBUG_IPT1A
                            SWCIPTable_dumpIPs(pThis, &pThis->m_aIPTable, &pThis->m_aIPTable2, 42, NULL);
#endif
#endif
                            SWCIPTable_convertIPtoPauseAngle(ipData, PAUSE_ANGLE_WEIGHT_FACTOR);
                        }
                    }
                    else if (pThis->m_backend->m_pSearchDB->algDeleteAngleCloseToAngle[ALG_MGD])
                    {
                        ET9BOOL deleteLesserAngle = (ET9BOOL)((keepPrevAngle) ? (ipData->m_fIPWeight < ANGLE_DELETION_THRESHOLD_WEIGHT) : (ipPrev->m_fIPWeight < ANGLE_DELETION_THRESHOLD_WEIGHT));
                        if (deleteLesserAngle)
                        {
                            if (!keepPrevAngle)
                            {
                                if (prevIndex > 0)      /* Sanity check */
                                {
                                    AlwaysAssert(ipPrev == pIPTable[prevIndex]); /* Make sure ipPrev set correctly for deletion */
#if DEBUG_IPT1D
                                    Log(SWLogger_DEBUG, L"*********> Angle/PauseAngle IP @ [%d, %d] {FI:%d  Wt: %.2f} NEAR IP @ [%d, %d] {FI:%d  Wt: %.2f} DELETED\n", ipPrev->m_IPLocation.x, ipPrev->m_IPLocation.y, ipPrev->m_IPPosIndex, ipPrev->m_fIPWeight, ipData->m_IPLocation.x, ipData->m_IPLocation.y, ipData->m_IPPosIndex, ipData->m_fIPWeight);
#if DEBUG_IPT1A
                                    SWCIPTable_dumpIPs(pThis, &pThis->m_aIPTable, &pThis->m_aIPTable2, 41, NULL);
#endif
#endif
                                    SWCIPTable_deleteIP(pThis, ipPrev, _TRUE, 9);
                                    ipData->m_IPIndex--;
                                    prevIndex--;            /* Reset ipPrev to what is now preceding IP */
                                    ipPrev = pIPTable[prevIndex];  /* get (new) preceding IP */
                                    if (ipPrev->m_IPType == Entry)
                                        ipData->m_exitIndex = ipPrev->m_exitIndex;
                                    if (ipData->m_IPIndex > 1)
                                        ipPrev2 = pIPTable[ipData->m_IPIndex - 2];
                                    else
                                        ipPrev2 = NULL;
                                    if (followIndex > 0)
                                    {
                                        followIndex--;
                                        AlwaysAssert(followIndex == pIPTable[followIndex]->m_IPIndex);
                                    }
                                }
                            }
                            else /* if (keepPrevAngle) */
                            {
#if DEBUG_IPT1D
                                Log(SWLogger_DEBUG, L"*********> Angle/PauseAngle IP @ [%d, %d] {FI:%d  Wt: %.2f} NEAR IP @ [%d, %d] {FI:%d  Wt: %.2f} DELETED (not added)\n", ipData->m_IPLocation.x, ipData->m_IPLocation.y, ipData->m_IPPosIndex, ipData->m_fIPWeight, ipPrev->m_IPLocation.x, ipPrev->m_IPLocation.y, ipPrev->m_IPPosIndex, ipPrev->m_fIPWeight);
#if DEBUG_IPT1A
                                SWCIPTable_dumpIPs(pThis, &pThis->m_aIPTable, &pThis->m_aIPTable2, 42, NULL);
#endif
#endif
                                SWCIPTable_deleteIP(pThis, ipData, _FALSE, 11);      /* Hasn't been added to Ptr array yet, so no erase() call needed */
                                return IPT_FAILURE;
                            }
                        }
                    }
                    else if ((ipData->m_MergeCount >= 1) && (ipPrev->m_MergeCount >= 1))
                    {
                        BYTE4 timeDif;
                        BYTE2 mergedX = ((ipPrev->m_IPLocation.x * ipPrev->m_MergeCount) + (ipData->m_IPLocation.x * ipData->m_MergeCount) + 1) / (ipData->m_MergeCount + ipPrev->m_MergeCount);
                        BYTE2 mergedY = ((ipPrev->m_IPLocation.y * ipPrev->m_MergeCount) + (ipData->m_IPLocation.y * ipData->m_MergeCount) + 1) / (ipData->m_MergeCount + ipPrev->m_MergeCount);
                        SBYTE2 mergedIPPos = ((ipPrev->m_IPPosIndex * ipPrev->m_MergeCount) + (ipData->m_IPPosIndex * ipData->m_MergeCount) + 1) / (ipData->m_MergeCount + ipPrev->m_MergeCount);
#if DEBUG_IPT1D
                        Log(SWLogger_DEBUG, L" Angle IPs @ [%d, %d] / [%d, %d] MERGED: [%d, %d]\n", ipPrev->m_IPLocation.x, ipPrev->m_IPLocation.y, ipData->m_IPLocation.x, ipData->m_IPLocation.y, mergedX, mergedY);
#endif
                        ipPrev->m_IPLocation.x = mergedX;
                        ipPrev->m_IPLocation.y = mergedY;
                        timeDif = (ipData->m_TimeOffset - ipPrev->m_TimeOffset) / (ipPrev->m_MergeCount + ipData->m_MergeCount);
                        ipPrev->m_TimeOffset = (BYTE4)(ipPrev->m_TimeOffset + (timeDif * ipData->m_MergeCount));
                        ipPrev->m_MergeCount = (BYTE1)(ipPrev->m_MergeCount + ipData->m_MergeCount);
                        ipPrev->m_FixedIndexOut = ipData->m_FixedIndexOut;
                        ipPrev->m_IPPosIndex = mergedIPPos;
                        ipPrev->m_fIPWeight = MultipleIPsWeight;
                        ipPrev->m_fSkipPenalty = SINGLE_KEY_TO_MERGED_IP;     /* Actual value set in GetSkippingPenalty()... */
                        ipPrev->m_RowFilled = CALCULATE_ROW;
                        SWCIPTable_deleteIP(pThis, ipData, _FALSE, 12);      /* Hasn't been added to Ptr array yet, so no erase() call needed */
                        SWCIPTable_DoProcessTableRows(pThis);
#if DEBUG_IPT1D
                        SWCIPTable_dumpIPs(pThis, &pThis->m_aIPTable, &pThis->m_aIPTable2, 13, NULL);
#endif
                        return IPT_FAILURE;
                    }
                }
            }
        }
    }
    /* If IP is being inserted, check for certain conflicts with the following IP */
    if ((swapPrev > 0) && (ipFollow != NULL))
    {
        if (!ipDataInDouble && !ipFollowInDouble)
        {
            /* Check for a bogus Angle IP that may have been added just after another Angle IP */
            if (((ipData->m_IPType == Angle) || (ipData->m_IPType == PauseAngle)) && ((ipFollow->m_IPType == Angle) || (ipFollow->m_IPType == PauseAngle)))
            {
                if (((ipFollow->m_FixedIndexIn - ipData->m_FixedIndexOut) <= ANGLE_ANGLE_MIN_INTERVALS) &&
                    (_SWPoint_distance(&ipFollow->m_IPLocation, &ipData->m_IPLocation) <= ANGLE_ANGLE_MIN_DISTANCE))
                {
                    if (pThis->m_backend->m_pSearchDB->algConvertAngleCloseToAngle[ALG_MGD])
                    {
                        if (ipData->m_IPType == Angle)
                        {
#if DEBUG_IPT1D
                            Log(SWLogger_DEBUG, L" Angle IP @ [%d, %d] NEAR IP @ [%d, %d] CONVERTED to PauseAngle IP\n", ipData->m_IPLocation.x, ipData->m_IPLocation.y, ipFollow->m_IPLocation.x, ipFollow->m_IPLocation.y);
                            SWCIPTable_dumpIPs(pThis, &pThis->m_aIPTable, &pThis->m_aIPTable2, 41, NULL);
#endif
                            SWCIPTable_convertIPtoPauseAngle(ipData, PAUSE_ANGLE_WEIGHT_FACTOR);
                        }
                        if (ipFollow->m_IPType == Angle)
                        {
#if DEBUG_IPT1D
                            Log(SWLogger_DEBUG, L" Angle IP @ [%d, %d] NEAR IP @ [%d, %d] CONVERTED to PauseAngle IP\n", ipFollow->m_IPLocation.x, ipFollow->m_IPLocation.y, ipData->m_IPLocation.x, ipData->m_IPLocation.y);
                            SWCIPTable_dumpIPs(pThis, &pThis->m_aIPTable, &pThis->m_aIPTable2, 42, NULL);
#endif
                            SWCIPTable_convertIPtoPauseAngle(ipFollow, PAUSE_ANGLE_WEIGHT_FACTOR);
                        }
                    }
                    else if ((ipData->m_MergeCount >= 1) && (ipFollow->m_MergeCount >= 1))
                    {
                        BYTE4 timeDif;
                        BYTE2 mergedX = ((ipFollow->m_IPLocation.x * ipFollow->m_MergeCount) + (ipData->m_IPLocation.x * ipData->m_MergeCount) + 1) / (ipData->m_MergeCount + ipFollow->m_MergeCount);
                        BYTE2 mergedY = ((ipFollow->m_IPLocation.y * ipFollow->m_MergeCount) + (ipData->m_IPLocation.y * ipData->m_MergeCount) + 1) / (ipData->m_MergeCount + ipFollow->m_MergeCount);
                        SBYTE2 mergedIPPos = ((ipFollow->m_IPPosIndex * ipFollow->m_MergeCount) + (ipData->m_IPPosIndex * ipData->m_MergeCount) + 1) / (ipData->m_MergeCount + ipFollow->m_MergeCount);
#if DEBUG_IPT1D
                        Log(SWLogger_DEBUG, L" Angle IPs @ [%d, %d] / [%d, %d] MERGED: [%d, %d]\n", ipData->m_IPLocation.x, ipData->m_IPLocation.y, ipFollow->m_IPLocation.x, ipFollow->m_IPLocation.y, mergedX, mergedY);
#endif
                        ipFollow->m_IPLocation.x = mergedX;
                        ipFollow->m_IPLocation.y = mergedY;
                        timeDif = (ipFollow->m_TimeOffset - ipData->m_TimeOffset) / (ipFollow->m_MergeCount + ipData->m_MergeCount);
                        ipFollow->m_TimeOffset = ipData->m_TimeOffset + (timeDif * ipFollow->m_MergeCount);
                        ipFollow->m_MergeCount = (BYTE1)(ipFollow->m_MergeCount + ipData->m_MergeCount);
                        ipFollow->m_FixedIndexOut = ipData->m_FixedIndexOut;
                        ipFollow->m_IPPosIndex = mergedIPPos;
                        ipFollow->m_fIPWeight = MultipleIPsWeight;
                        ipFollow->m_fSkipPenalty = SINGLE_KEY_TO_MERGED_IP;     /* Actual value set in GetSkippingPenalty()... */
                        ipFollow->m_RowFilled = CALCULATE_ROW;
                        SWCIPTable_deleteIP(pThis, ipData, _FALSE, 12);      /* Hasn't been added to Ptr array yet, so no erase() call needed */
                        SWCIPTable_DoProcessTableRows(pThis);
#if DEBUG_IPT1D
                        SWCIPTable_dumpIPs(pThis, &pThis->m_aIPTable, &pThis->m_aIPTable2, 13, NULL);
#endif
                        return IPT_FAILURE;
                    }
                }
            }
            /* Check for Pause near Angle IP (or PauseAngle IP, since we still want to delete the Pause IP) and convert to PauseAngle if found */
            else if ( ( ((ipData->m_IPType == Angle) || (ipData->m_IPType == PauseAngle)) && (ipFollow->m_IPType == Pause) ) ||
                      ( (ipData->m_IPType == Pause) && ((ipFollow->m_IPType == Angle) || (ipFollow->m_IPType == PauseAngle)) ) )
            {
                if (_SWPoint_distance(&ipFollow->m_IPLocation, &ipData->m_IPLocation) < SOFTENING_DISTANCE)
                {
                    if (ipFollow->m_IPType == Pause)
                    {
                        if (ipData->m_IPType == Angle)      /* Don't adjust again if already merged with another Pause */
                        {
                            SWCIPTable_convertIPtoPauseAngle(ipData, PAUSE_ANGLE_WEIGHT_FACTOR);
                        }
                        ipData->m_IPFlags = (PAUSE_IP | ANGLE_IP | MULTIPLE_IP);
                        if (prevIndex > 0)      /* Sanity check */
                        {
                            AlwaysAssert(ipFollow == pIPTable[followIndex]); /* Make sure ipFollow set correctly for deletion */
#if DEBUG_IPT1A
                            Log(SWLogger_DEBUG, L" Deleting Pause IP @ [%d, %d], convert Angle to PauseAngle\n", ipFollow->m_IPLocation.x, ipFollow->m_IPLocation.y);
#endif
                            SWCIPTable_deleteIP(pThis, ipFollow, _TRUE, 13);
                            if (followIndex < pThis->wIPCount)
                            {
                                ipFollow = pIPTable[followIndex];  /* get (new) following IP */
                                AlwaysAssert(followIndex == pIPTable[followIndex]->m_IPIndex);
                            }
                            else
                            {
                                ipFollow = NULL;
                                followIndex = 0;
                            }
                        }
                    }
                    else
                    {
#if DEBUG_IPT1A
                        Log(SWLogger_DEBUG, L" Deleting Pause IP @ [%d, %d], convert Angle to PauseAngle\n", ipData->m_IPLocation.x, ipData->m_IPLocation.y);
#endif
                        if (ipFollow->m_IPType == Angle)        /* Don't adjust again if already merged with another Pause */
                        {
                            SWCIPTable_convertIPtoPauseAngle(ipFollow, PAUSE_ANGLE_WEIGHT_FACTOR);
                        }
                        ipFollow->m_IPFlags = (PAUSE_IP | ANGLE_IP | MULTIPLE_IP);
                        SWCIPTable_deleteIP(pThis, ipData, _FALSE, 14);      /* Hasn't been added to Ptr array yet, so no erase() call needed */
#if DEBUG_IPT1A
                        SWCIPTable_dumpIPs(pThis, &pThis->m_aIPTable, &pThis->m_aIPTable2, 14, NULL);
#endif
                        return IPT_FAILURE;
                    }
                }
            }
        }
    }
    /* Set fields in previous and current IP that can now be set */
    if (ipPrev != NULL)
    {
        /* Time to signal Z1 input (if not already signalled) since any other IP verifies that this is not a Tap-Shift stroke */
        if ((SWCIPAnalyzer_IsCheckTap(pThis->m_backend->pIPAnalyzer) || SWCIPAnalyzer_IsCheckTapHold(pThis->m_backend->pIPAnalyzer)) &&
            (ipData->m_IPType != Exit) && (ipData->m_IPType != Pause) &&
            (ipData->m_IPType != PenUp) && (ipData->m_IPType != SoftUp))
        {
            /* Don't signal Z1 input if this can still be a key-rubbing gesture */
            SWCIPTable_CheckSignalDetectZ1Input(pThis);
        }
        if (ipData->m_IPType != Exit)
            ipData->m_IPPosIndex = (ipData->m_FixedIndexIn + ipData->m_FixedIndexOut) / 2;
        else
            ipData->m_IPPosIndex = ipData->m_FixedIndexIn;
        /* If y-coordinate position is below the bottom of the keyboard, then identify the first prior point on */
        /*   the path below the bottom edge of the keyboard */
        if (ipData->m_IPLocation.y > SWDbm_keyboardHeight(pDbm))
        {
            SBYTE2   bottomIndex;
            SBYTE2   origIndex;
            _SWPoint exitLoc;
            ipData->bottomRow = _TRUE;
            bottomIndex = ipData->m_FixedIndexIn;   /* Find point where path first exits from bottom of keyboard */
            exitLoc = SWCIPAnalyzer_GetFixedPoint(pThis->m_backend->pIPAnalyzer, bottomIndex, 7);
            origIndex = bottomIndex;
            while ((exitLoc.y > SWDbm_keyboardHeight(pDbm)) && (bottomIndex > 0))
            {
                bottomIndex--;
                exitLoc = SWCIPAnalyzer_GetFixedPoint(pThis->m_backend->pIPAnalyzer, bottomIndex, 8);
            }
            if (bottomIndex > 0)
            {
                if (bottomIndex < origIndex)        /* Make sure we backed up at least 1 index */
                    bottomIndex++;                  /* Use first fixed point below bottom of keyboard */
                ipData->exitLoc = SWCIPAnalyzer_GetFixedPoint(pThis->m_backend->pIPAnalyzer, bottomIndex, 9);
            }
        }
        else
            ipData->bottomRow = _FALSE;
        ipData->m_fLengthFactor = (float)1.0;   /*Init to default values */
        ipData->m_fLengthRatio = (float)1.0;
        {
            SBYTE2 prevFixedOut = ipPrev->m_FixedIndexOut;
            SBYTE2 thisFixedIn = ipData->m_FixedIndexIn;
            if (prevFixedOut < thisFixedIn)
            {
                _SWPoint p1, p2;
                p1 = SWCIPAnalyzer_GetFixedPoint(pThis->m_backend->pIPAnalyzer, prevFixedOut, 10);
                p2 = SWCIPAnalyzer_GetFixedPoint(pThis->m_backend->pIPAnalyzer, thisFixedIn, 11);
                ipData->m_LineLen8 = _SWPoint_distance8(&p1, &p2);
                ipData->m_SegmentLen8 = SWCIPAnalyzer_GetPathLength8(pThis->m_backend->pIPAnalyzer, prevFixedOut, thisFixedIn);
                if (ipData->m_SegmentLen8 > ipData->m_LineLen8)
                {
                    /* Set m_fLengthFactor as the (constrained) value of the ratio of (m_SegmentLen8/m_LineLen8) */
                    ipData->m_fLengthRatio = (float)ipData->m_SegmentLen8 / (float)ipData->m_LineLen8;
                    /* SavedCodeSegments/289 - Previous calculation of m_fLengthFactor */
                    ipData->m_fLengthFactor = ipData->m_fLengthRatio;
                    if (ipData->m_fLengthFactor > LENGTH_FACTOR_MAX)
                        ipData->m_fLengthFactor = LENGTH_FACTOR_MAX;
                }
            }
        }
        /* If first exit immediately follows PenDown, and path to Exit is relatively straight,  */
        /* flag as firstLetterShiftGesture, so that it should apply to initial letter of word rather than the second */
        if ((pThis->m_ShiftLocCount == 1) && (ipData->m_IPType == Exit) && (ipPrev->m_IPType == PenDown))
        {
            if (ipData->m_fLengthRatio < SIGNIFICANT_CURVATURE_RATIO)
                pThis->firstLetterShiftGesture = _TRUE;
        }
        if (ipPrev->m_IPType == Entry)
        {
            if ((pThis->lastExitIP != NULL) && (ipData->m_IPType != Exit) && (swapPrev == 0))
            {
                /*x This was failing in SwypeTest: AlwaysAssert(ipData->m_exitIndex == lastExitIP->m_IPIndex); */
                ipData->m_exitIndex = pThis->lastExitIP->m_IPIndex;
            }
            else
            {
                if (!DebugAssert(ipData->m_exitIndex == ipPrev->m_exitIndex))
                    ipData->m_exitIndex = ipPrev->m_exitIndex;
            }
        }
#if DEBUG_IPT1A
        if ((ipData->m_IPType == PenUp) || (ipData->m_IPType == Tap))   /* Add extra blank line after PenUp... */
            Log(SWLogger_DEBUG, L" \n");
#endif
        /* See if we need to repair the values previously set in ipFollow, since it now follows ipData */
        if (swapPrev > 0)
        {
            /* Adjust index if swapped into preceding position */
            ipData->m_IPIndex = (SBYTE2)(ipData->m_IPIndex - swapPrev);
            if (followIndex > 0)
            {
                AlwaysAssert(ipFollow == pIPTable[followIndex]); /* Make sure ipFollow set correctly for deletion */
                ipFollow->m_RowFilled = CALCULATE_ROW;      /* At minimum, need to re-do segment matches, since segment is now shorter... */
                ipFollow->m_fLengthFactor = (float)1.0; /*Init to default values */
                ipFollow->m_fLengthRatio = (float)1.0;
                {
                    SBYTE2 prevFixedOut = ipData->m_FixedIndexOut;
                    SBYTE2 thisFixedIn = ipFollow->m_FixedIndexIn;
                    if (prevFixedOut < thisFixedIn)
                    {
                        _SWPoint p1, p2;
                        p1 = SWCIPAnalyzer_GetFixedPoint(pThis->m_backend->pIPAnalyzer, prevFixedOut, 12);
                        p2 = SWCIPAnalyzer_GetFixedPoint(pThis->m_backend->pIPAnalyzer, thisFixedIn, 13);
                        ipFollow->m_LineLen8 = _SWPoint_distance8(&p1, &p2);
                        ipFollow->m_SegmentLen8 = SWCIPAnalyzer_GetPathLength8(pThis->m_backend->pIPAnalyzer, prevFixedOut, thisFixedIn);
                        if (ipFollow->m_SegmentLen8 > ipFollow->m_LineLen8)
                        {
                            /* Set m_fLengthFactor as a function of the ratio of (m_SegmentLen8/m_LineLen8) */
                            ipFollow->m_fLengthRatio = (float)ipFollow->m_SegmentLen8 / (float)ipFollow->m_LineLen8;
                            /* SavedCodeSegments/290 - Previous calculation of m_fLengthFactor */
                            ipFollow->m_fLengthFactor = ipData->m_fLengthRatio;
                            if (ipFollow->m_fLengthFactor > LENGTH_FACTOR_MAX)
                                ipFollow->m_fLengthFactor = LENGTH_FACTOR_MAX;
                        }
                    }
                }
                if (ipData->m_IPType == Entry)
                    ipFollow->m_exitIndex = ipData->m_exitIndex;
                else
                    ipFollow->m_exitIndex = -1;
            }
        }
    }
    /* Add the TableRow object to the table... */
    if (ipData->m_IPIndex < pThis->wIPCount)          /* Shift Table rows if IP added between existing IPs */
    {
        AlwaysAssert(ipData->m_IPType != Exit);
#if DEBUG_IPT1A
        if (swapPrev > 0)
            Log(SWLogger_DEBUG, L"  swapPrev == %d!!\n", swapPrev);
        Log(SWLogger_DEBUG, L"  Inserting IP of type %d at position %d in IP Table!!\n", ipData->m_IPType, ipData->m_IPIndex);
#endif
        /* Add new row at proper position */
        SWCIPTableRow_insert(__CONTEXT, pThis->m_aIPTable, ipData->m_IPIndex, &pThis->wIPCount, _SWYPE_MAX_IPS, ipData);
        /* loop through each IP Table data item that we have not yet processed */
        {
            SBYTE2 index;
            for (index = (ipData->m_IPIndex + 1); index < pThis->wIPCount; index++)
            {
                SWCIPTableRow *ipAccess = pThis->m_aIPTable[index];
                ipAccess->m_IPIndex++;  /* Adjust internal indices of shifted rows */
                if (ipAccess->m_exitIndex >= ipData->m_IPIndex)     /* Adjust exitIndex if Exit IP was also shifted */
                    ipAccess->m_exitIndex++;
            }
        }
        {
            SBYTE2 doubleIPIndex;
            for (doubleIPIndex = 0; doubleIPIndex < pThis->m_wDoubleIPCount; doubleIPIndex++) /* Adjust double IP included indices above inserted IP */
            {
                SWCIPTableRow *ipDoubleAccess = pThis->m_aIPTable2[doubleIPIndex];
                if (!includeNewIPinDouble || (ipDoubleAccess != ipDouble))
                {
                    if (ipDoubleAccess->gestureIPIndexIn >= ipData->m_IPIndex)
                        ipDoubleAccess->gestureIPIndexIn++;
                    if (ipDoubleAccess->gestureIPIndexOut >= ipData->m_IPIndex)
                        ipDoubleAccess->gestureIPIndexOut++;
                }
            }
        }
    }
    else
    {
        /* Add new row to end of array */
        SWCIPTableRow_append(__CONTEXT, pIPTable, &pThis->wIPCount, _SWYPE_MAX_IPS, ipData);
    }
    if (includeNewIPinDouble)
    {
        ipData->m_DoubleIPIndex = -ipDouble->m_DoubleIPIndex;
        SWCIPTable_recordIncludedIP(pThis, ipDouble, ipData, 7);
    }

    SWCIPTable_DoProcessTableRows(pThis);
#if (DEBUG_IPT1A || DEBUG_IPT1B)
    SWCIPTable_dumpIPs(pThis, &pThis->m_aIPTable, &pThis->m_aIPTable2, 15, NULL);
#endif

    return IPT_OK;
} /* SWCIPTable_AddIPtoTable(SWCIPTableRow *ipData) */



/*============================================================================= */
/* Function:    SWCIPTable_NewIPTableRow() */
/* Returns:     SWCIPTableRow * - Pointer to available IP Table Row object */
/* Description: Return the appropriate data item from the SWCIPTableRow object */
/*              for the IP indicated by the IPIndex parameter. */
/*============================================================================= */

SWCIPTableRow* ET9FARCALL SWCIPTable_NewIPTableRow(SWCIPTable *pThis, _SWPoint IPLocation, IPType IPKind, SBYTE2 wIPIndex, BYTE4 dwTimeOffset,
                        SBYTE2 wFixedIndexIn, SBYTE2 wFixedIndexOut, SBYTE2 debugDataVal, ET9BOOL checkForPathDivision)
{
    SWCIPTableRow *ipData, *ipPrev = NULL, *ipCreate;
    ET9BOOL    ipCreated = _FALSE;

    ipCreate = SWCIPTable_ObtainIPTableRow(pThis, IPLocation, IPKind, wIPIndex, dwTimeOffset, wFixedIndexIn, wFixedIndexOut, debugDataVal);
    if (ipCreate == &pThis->m_IPTableRowOverflow) {
        return ipCreate;
    }
    if (checkForPathDivision)
    {
        AlwaysAssert(wIPIndex == SWCIPTable_GetIPTableSize(pThis));
        if (wIPIndex > 0)
        {
            SBYTE2  wIPIndexM1 = wIPIndex - 1;
            ipPrev = SWCIPTable_GetIPTableRow(pThis, &wIPIndexM1);
                                                        /*                            And, while we're at it, is wIPIndex ok? Only IPAnalyser knows for sure. That we have 8 (arg, arg, arg...) is a clue. */
            if (ipPrev->m_IPType == Exit)
                checkForPathDivision = _FALSE;
        }
        else
            checkForPathDivision = _FALSE;
    }
    /* Don't create PathDivision IPs in the middle of a short segment */
    if (checkForPathDivision)
    {
        if ((wFixedIndexIn - ipPrev->m_FixedIndexOut) < MIN_PATH_DIVISION_FIXED_INTERVALS)
            checkForPathDivision = _FALSE;
    }
    /* Don't create PathDivision IPs in the middle of a DoubleLetter gesture */
    if (checkForPathDivision)
    {
        if (pThis->m_wDoubleIPCount > 0)
        {
            SWCIPTableRow *ipDouble = pThis->m_aIPTable2[pThis->m_wDoubleIPCount - 1];
            if (SWCIPTable_includeAdjacentIP(ipDouble, ipCreate))
            {
                ipCreate->m_DoubleIPIndex = 0;      /* Reset to 0 prior to subsequent ADDIPtoTable() call */
                checkForPathDivision = _FALSE;
            }
        }
    }
    /* Determine whether path curves back on itself, and if so, create one or more PathDivision IPs between identified sub-segments */
    if (checkForPathDivision)
    {
        SWFixedData *pathLoc, *lastPathLoc, *endLoc, *maxD2Loc;
        SBYTE2  subSegmentCount = 1;
        SBYTE2  fi, originIndex;        /* Fixed index iterator */
        SBYTE2  lineLen8, segmentLen8;
        float   lengthRatio;
        ET9BOOL perform2ndCheck;
        SBYTE4  maxD2Sum;
        pThis->wPathPointCount = 0;
        pathLoc = SWCIPAnalyzer_GetFixedData(pThis->m_backend->pIPAnalyzer, ipPrev->m_FixedIndexOut, 59);
        pathLoc->m_Flags = X_AND_Y_NO_CHANGE;               /* Add initial path point as first segment boundary point */
        SWFixedData_append(pThis, pathLoc);
        endLoc = SWCIPAnalyzer_GetFixedData(pThis->m_backend->pIPAnalyzer, wFixedIndexIn, 60);
        /* Calculate length ratio for intended segment.  Perform additional checks if it exceeds MAX_CURVATURE_RATIO threshold */
        /*   but no PathDivision IP is determined initially. */
        {
            _SWPoint *point0 = &pThis->m_aPathPoints[0]->m_Point;
            lineLen8 = _SWPoint_distance8(point0, &endLoc->m_Point);
        }
        segmentLen8 = SWCIPAnalyzer_GetPathLength8(pThis->m_backend->pIPAnalyzer, ipPrev->m_FixedIndexOut, wFixedIndexIn);
        lengthRatio = (float)segmentLen8 / (float)lineLen8;
        perform2ndCheck = (ET9BOOL)(lengthRatio > PATH_DIVISION_CURVATURE_RATIO);
        /* Skip over first Z1_DIFFERENCE_OFFSET points of path segment to avoid path inconsistencies near */
        /*   preceding IP */
        originIndex = ipPrev->m_FixedIndexOut + Z1_DIFFERENCE_OFFSET;
        if (originIndex > wFixedIndexIn)
            originIndex = wFixedIndexIn;
        lastPathLoc = SWCIPAnalyzer_GetFixedData(pThis->m_backend->pIPAnalyzer, originIndex, 61);
        lastPathLoc->m_Flags = X_AND_Y_NO_CHANGE;
        maxD2Sum = lastPathLoc->D2Sum;
        maxD2Loc = lastPathLoc;
        for (fi = originIndex + 1; (fi <= (wFixedIndexIn - Z1_DIFFERENCE_OFFSET)); fi++)
        {
            pathLoc = SWCIPAnalyzer_GetFixedData(pThis->m_backend->pIPAnalyzer, fi, 62);
            pathLoc->m_Flags = X_AND_Y_NO_CHANGE;
            if (pathLoc->D2Sum > maxD2Sum)
            {
                maxD2Sum = pathLoc->D2Sum;
                maxD2Loc = pathLoc;
            }
            /* If both X and Y deltas change sign, create zero-length sub-segment since otherwise */
            /* the two consecutive (but potentially intersecting) sub-segments would not be tested */
            if ((lastPathLoc->D1x >= 0) != (pathLoc->D1x >= 0))
            {
                SWFixedData_append(pThis, lastPathLoc);
                if (lastPathLoc->D1x >= 0)
                    lastPathLoc->m_Flags = X_POS_TO_NEG;
                else
                    lastPathLoc->m_Flags = X_NEG_TO_POS;
                subSegmentCount++;
            }
            if ((lastPathLoc->D1y >= 0) != (pathLoc->D1y >= 0))
            {
                SWFixedData_append(pThis, lastPathLoc);
                if (lastPathLoc->m_Flags != X_AND_Y_NO_CHANGE)
                    lastPathLoc->m_Flags |= X_AND_Y_BOTH_CHANGE;
                if (lastPathLoc->D1y >= 0)
                    lastPathLoc->m_Flags |= Y_POS_TO_NEG;
                else
                    lastPathLoc->m_Flags |= Y_NEG_TO_POS;
                subSegmentCount++;
            }
            lastPathLoc = pathLoc;
        }
        endLoc->m_Flags = X_AND_Y_NO_CHANGE;
        SWFixedData_append(pThis, endLoc);  /* Final sub-segment ends at new IP location */
        if (subSegmentCount >= 3)   /* Need at least 3 sub-segments to be able to check alternating sub-segments */
        {
            SBYTE2  seg1, seg2;     /* Iterate through indices of initial points of segment pairs */
            _SWPoint q;              /* Point of intersection, if any */
            BYTE2   minDistance = DISTANCE8_MAX;
            SBYTE2  minIndex = 0;
            _SWPoint closePoint;
            SBYTE2  intersect, segIndex1 = 0, segIndex2 = 0;
            BYTE2   segDistanceThreshold = (BYTE2)(2 * SegmentThreshold8_2);
            for (seg1 = 0; seg1 < (subSegmentCount - 2); seg1++)
            {
                for (seg2 = (seg1 + 2); seg2 < subSegmentCount; seg2++)
                {
                    _SWPoint *p1 = &pThis->m_aPathPoints[seg1]->m_Point;
                    _SWPoint *p2 = &pThis->m_aPathPoints[seg1 + 1]->m_Point;
                    _SWPoint *p3 = &pThis->m_aPathPoints[seg2]->m_Point;
                    _SWPoint *p4 = &pThis->m_aPathPoints[seg2 + 1]->m_Point;
                    ET9BOOL createPathDivision = _FALSE;
                    intersect = _SWPoint_intersectionPoint(&pThis->m_backend->sScreenGeometry, &q, p1, p2, p3, p4);
                    if (intersect != NON_INTERSECTING)
                    {
                        if ((intersect >= CHECK_P1) && (intersect <= CHECK_P4))
                        {
                            switch (intersect)
                            {
                            case CHECK_P1:
                                closePoint = pThis->m_aPathPoints[seg1]->m_Point;
                                segIndex1 = pThis->m_aPathPoints[seg2]->m_Index;
                                segIndex2 = pThis->m_aPathPoints[seg2 + 1]->m_Index;
                                break;
                            case CHECK_P2:
                                closePoint = pThis->m_aPathPoints[seg1 + 1]->m_Point;
                                segIndex1 = pThis->m_aPathPoints[seg2]->m_Index;
                                segIndex2 = pThis->m_aPathPoints[seg2 + 1]->m_Index;
                                break;
                            case CHECK_P3:
                                closePoint = pThis->m_aPathPoints[seg2]->m_Point;
                                segIndex1 = pThis->m_aPathPoints[seg1]->m_Index;
                                segIndex2 = pThis->m_aPathPoints[seg1 + 1]->m_Index;
                                break;
                            case CHECK_P4:
                                closePoint = pThis->m_aPathPoints[seg2 + 1]->m_Point;
                                segIndex1 = pThis->m_aPathPoints[seg1]->m_Index;
                                segIndex2 = pThis->m_aPathPoints[seg1 + 1]->m_Index;
                                break;
                            }
                            minDistance = DISTANCE8_MAX;
                            minIndex = 0;
                            for (fi = segIndex1; fi <= segIndex2; fi++)
                            {
                                _SWPoint *fiPt = &SWCIPAnalyzer_GetFixedData(pThis->m_backend->pIPAnalyzer, fi, 63)->m_Point;
                                BYTE2 pathDistance = _SWPoint_distance8(&closePoint, fiPt);
                                if (pathDistance < minDistance)
                                {
                                    minDistance = pathDistance;
                                    minIndex = fi;
                                }
                            }
                            if (minDistance <= segDistanceThreshold)
                            {
                                createPathDivision = _TRUE;
                            }
                        }
                        else
                        {
                            /* If the segments actually intersect, then we need to break at the end of the preceding segment (seg2 - 1) */
                            /* rather than at the start of the intersecting segment (seg2) */
                            createPathDivision = _TRUE;
                            seg2--;
                        }
                    }
                    if (createPathDivision)
                    {
                        SBYTE2 breakIndex = pThis->m_aPathPoints[seg2]->m_Index;
                        /* Create a PathDivision IP at start of seg2 */
                        ipData = SWCIPTable_ObtainIPTableRow(pThis, pThis->m_aPathPoints[seg2]->m_Point, PathDivision, wIPIndex, pThis->m_aPathPoints[seg2]->m_TimeOffset, breakIndex, breakIndex, (debugDataVal + 100));
                        if (ipData == &pThis->m_IPTableRowOverflow) {
                            return ipData;
                        }
                        ipData->m_RowFilled = CALCULATE_ROW;
                        SWCIPTable_AddIPtoTable(pThis, ipData);
#if DEBUG_IPT6
                        Log(SWLogger_DEBUG, L" [{%d}] PathDivision IP[%d] FROM <%d> ADDED @ [%d, %d] ", debugDataVal, wIPIndex, intersect, ipData->m_IPLocation.x, ipData->m_IPLocation.y);
                        Log(SWLogger_DEBUG, L" {%d == %d} after segment %d of %d {%d - %d}\n", ipData->m_IPPosIndex, breakIndex, seg2, subSegmentCount, originIndex, wFixedIndexIn);
#endif
                        wIPIndex = SWCIPTable_GetIPTableSize(pThis);            /* Adjust IPIndex for IP to be created below */
                        originIndex = breakIndex;       /* Save new origin for above check in case another segment is split off */
                        seg1 = seg2;        /* Path has been split at start of seg2.  Continue from there if enough more segments remain. */
                        seg2 = seg1 + 2;
                        ipCreated = _TRUE;
                    }
                }
            }
            /* Check each delta point */
            if (!ipCreated && perform2ndCheck)
            {
                SBYTE2 breakIndex = maxD2Loc->m_Index;
                /* If proposed break location is not within a threshold of the center of the segment, use the center location instead */
                SBYTE2 middleThreshold = (wFixedIndexIn - ipPrev->m_FixedIndexOut) / 5;  /* OK if within middle 60% of path */
                if ((breakIndex < (ipPrev->m_FixedIndexOut + middleThreshold)) || (breakIndex > (wFixedIndexIn - middleThreshold)))
                {
                    breakIndex = (wFixedIndexIn + ipPrev->m_FixedIndexOut) / 2;
                    maxD2Loc = SWCIPAnalyzer_GetFixedData(pThis->m_backend->pIPAnalyzer, breakIndex, 64);
                }
                /* Create a PathDivision IP at breakIndex */
                ipData = SWCIPTable_ObtainIPTableRow(pThis, maxD2Loc->m_Point, PathDivision, wIPIndex, maxD2Loc->m_TimeOffset, breakIndex, breakIndex, (debugDataVal + 200));
                if (ipData == &pThis->m_IPTableRowOverflow) {
                    return ipData;
                }
                ipData->m_RowFilled = CALCULATE_ROW;
                SWCIPTable_AddIPtoTable(pThis, ipData);
#if DEBUG_IPT6
                Log(SWLogger_DEBUG, L" [{%d}] PathDivision IP[%d] FROM MaxD2Loc ADDED @ [%d, %d] {%d == %d} after %d segments {%d - %d}\n", debugDataVal, wIPIndex, ipData->m_IPLocation.x, ipData->m_IPLocation.y,
                    ipData->m_IPPosIndex, breakIndex, subSegmentCount, ipPrev->m_FixedIndexOut, wFixedIndexIn);
#endif
                wIPIndex = SWCIPTable_GetIPTableSize(pThis);            /* Adjust IPIndex for IP to be created below */
            }
        }
    }
    ipCreate->m_IPIndex = wIPIndex;     /* wIPIndex may have been incremented since original creation */
    return ipCreate;

} /* SWCIPTable_NewIPTableRow() */



/*============================================================================= */
/* Function:    SWCIPTable_ObtainIPTableRow() */
/* Returns:     SWCIPTableRow * - Pointer to available IP Table Row object */
/* Description: Return the appropriate data item from the SWCIPTableRow object */
/*              for the IP indicated by the IPIndex parameter. */
/*============================================================================= */

SWCIPTableRow* ET9FARCALL SWCIPTable_ObtainIPTableRow(SWCIPTable *pThis, _SWPoint IPLocation, IPType IPKind, SBYTE2 wIPIndex, BYTE4 dwTimeOffset,
                        SBYTE2 wFixedIndexIn, SBYTE2 wFixedIndexOut, SBYTE2 debugDataVal)
{
    SWCIPTableRow *ipData;

    if (pThis->wIPPoolCount < _SWYPE_MAX_IPS) {
        ipData = &pThis->m_IPTableRowPool[pThis->wIPPoolCount++];
#if __ET9A_DEBUG_MEM_SIZE
        {
            if (pThis->wIPPoolCount > nMaxPoolSize) {
                nMaxPoolSize = pThis->wIPPoolCount;
                if (nMaxPoolSize % 10 == 0) {
                    Log(SWLogger_DEBUG, TEXT("Cap %d row pool now at %d.\n"), _SWYPE_MAX_IPS, nMaxPoolSize);
                }
            }
        }
#endif
    }
    else {
        /* Return a bogus result, and set bPanic so we can eventually abort */
        ipData = &pThis->m_IPTableRowOverflow;
        ipData->m_TimeOffset = 0xDEADBEEF;
        Log(SWLogger_DEBUG, SWTEXT("Panic: no more IP slots.\n"));
        _ET9AWSP_DATA->bPanic = _TRUE;
    }

    SWCIPTableRow_Construct(ipData, IPLocation, IPKind, wIPIndex, dwTimeOffset, wFixedIndexIn, wFixedIndexOut, debugDataVal);

    ipData->m_IPFlags = 0;
    ipData->m_RowFilled = CALCULATE_ROW;
    ipData->bottomRow =_FALSE;
    ipData->signChangesOK = _FALSE;
    ipData->m_MergeCount = 1;
    ipData->m_IPPosition = wFixedIndexOut * Z1_FIXED_INTERVAL;      /*lint !e734 */
    ipData->m_SegmentLen8 = 0;
    ipData->m_LineLen8 = 0;
    ipData->m_ForwardMultipleCount = ipData->m_BackwardMultipleCount = ipData->m_DoubleIPIndex = 0;
    ipData->gestureLoc.x = ipData->gestureLoc.y = 0;
    ipData->m_exitIndex = -1;
    ipData->m_fIPWeight = (float)0.0;
    ipData->m_fSkipPenalty = (float)0.0;
    ipData->m_fLengthRatio = ipData->m_fLengthFactor = ipData->gestureRatio = (float)1.0;
    ipData->circleScore = (float)0.0;
    ipData->gestureIPCount = 0;
    ipData->gestureIn = ipData->gestureOut = -1;                /* Init to invalid values */
    ipData->gestureIPIndexIn = ipData->gestureIPIndexOut = -1;
    ipData->m_IPPosIndex = (wFixedIndexIn + wFixedIndexOut) / 2;
    ipData->rubbedKey = (BYTE1)SWDbm_KD_INVALID_KEY;
    {
        SBYTE2 i;
        for (i = 0; i < N_KEYS_MAX; i++)
        {
            ipData->m_IPWDistance8[i] = 0;
            ipData->m_IPDistance8[i] = 0;
            ipData->m_IPAdjDistance8[i] = 0;
            ipData->m_SegmentWDistance8[i] = 0;
            ipData->m_SegmentDistance8[i] = 0;
            ipData->m_LinePos[i] = UNINITIALIZED;
            ipData->m_D2LocalMaxPos[i] = UNINITIALIZED;
            ipData->m_SegmentWeight[i] = (float)0.0;
        }
    }
    return ipData;

} /* SWCIPTable_ObtainIPTableRow() */



/*============================================================================= */
/* Function:    SWCIPTable_GetIPTableRow() */
/* Parameters:  IPIndex - Index of IP for which data is to be obtained. If this */
/*              is not a currently valid index, it is reset to a valid value. */
/* Returns:     SWCIPTableRow * - Pointer to corresponding IP Table Row */
/* Description: Return the appropriate data item from the SWCIPTableRow object */
/*              for the IP indicated by the IPIndex parameter. */
/*============================================================================= */

SWCIPTableRow* ET9FARCALL SWCIPTable_GetIPTableRow(SWCIPTable *pThis, SBYTE2 *IPIndex)
{
    SWCIPTableRow *ipData;

    /*ASSERT ((*IPIndex >= 0) && (*IPIndex < SWCIPTable_GetIPTableSize(pThis))); */
    SWCIPTableRow **pIPTable = SWCIPTable_GetIPTableArray(pThis);
    SBYTE2 tableSize = pThis->wIPCount;
    if (tableSize <= 0)
    {
        SWCIPTableRow *errorIPData = (SWCIPTableRow *)__ET9AWSP_malloc(pThis->m_backend->pContext, sizeof(SWCIPTableRow));
        SWCIPTableRow_Construct(errorIPData, _SWPoint_Construct_SSB2SB2(0, 0), Tap, 0, 0, (SBYTE2) 0, (SBYTE2) 0, 18);
        errorIPData->m_TimeOffset = 0;
        SWCIPTableRow_append(__CONTEXT, pIPTable, &pThis->wIPCount, _SWYPE_MAX_IPS, errorIPData);
        *IPIndex = 0;
    }
    if (*IPIndex < 0)
    {
        _ET9ASW_Trace(SWTracer::TT_CIPTable, (BYTE2)*IPIndex, StrToBYTE4((Str)TEXT("BADTBB")));
        _ET9ASW_Trace(SWTracer::TT_CIPTable, tableSize, StrToBYTE4((Str)TEXT("CURTBB")));
        *IPIndex = 0;
    }
    else if (*IPIndex >= tableSize)
    {
        _ET9ASW_Trace(SWTracer::TT_CIPTable, (BYTE2)*IPIndex, StrToBYTE4((Str)TEXT("BADTBC")));
        _ET9ASW_Trace(SWTracer::TT_CIPTable, tableSize, StrToBYTE4((Str)TEXT("CURTBC")));
        *IPIndex = tableSize - 1;
    }
    ipData = pIPTable[*IPIndex];
    return ipData;

} /* SWCIPTable_GetIPTableRow() */



/*============================================================================= */
/* Function:    SWCIPTable_GetIPTable2Row() */
/* Parameters:  IPIndex - Index of IP for which data is to be obtained. */
/* Returns:     SWCIPTableRow * - Pointer to corresponding IP Table2 Row */
/* Description: Return the appropriate data item from the SWCIPTableRow object */
/*              for the IP indicated by the IPIndex parameter. */
/*============================================================================= */

SWCIPTableRow* ET9FARCALL SWCIPTable_GetIPTable2Row(SWCIPTable *pThis, SBYTE2 IPIndex)
{
    SWCIPTableRow *ipData;
    SBYTE2 tableSize;

    /*ASSERT ((IPIndex >= 0) && (IPIndex < SWCIPTable_GetIPTableSize(pThis))); */
    SWCIPTable_GetIPTableArray(pThis);
    tableSize = pThis->m_wDoubleIPCount;
    if ((tableSize <= 0) || (IPIndex >= tableSize))
    {
        return (NULL);
    }
    ipData = pThis->m_aIPTable2[IPIndex];
    return ipData;

} /* SWCIPTable_GetIPTable2Row() */

/*============================================================================= */
/* Function:    SWCIPTable_RemoveIPTable2Row() */
/* Parameters:  IPIndex - Index of IP for which data is to be removed. */
/* Description: Remove the appropriate data item from the SWCIPTableRow object */
/*              for the IP indicated by the IPIndex parameter, and delete it. */
/*============================================================================= */

void ET9FARCALL SWCIPTable_RemoveIPTable2Row(SWCIPTable *pThis, SBYTE2 IPIndex)
{
    SWCIPTableRow_erase(__CONTEXT, pThis->m_aIPTable2, IPIndex, &pThis->m_wDoubleIPCount);

} /* SWCIPTable_RemoveIPTable2Row() */

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                                 
 *
 *                                                                                               
 *                                                                    
 *                                                                   
 *                                                       
 *                                                       
 *                                                  
 *
 *             
 */

void ET9FARCALL SWCIPTableRow_insert(_ET9AWSPContext *pContext,
                       SWCIPTableRow **pRowTable,
                       ET9S16 nIndex,
                       ET9S16 *pnCount,
                       ET9S16 nCapacity,
                       SWCIPTableRow *pItem)
{
    /* Out of space */
    if (*pnCount >= nCapacity) {
        Log(SWLogger_DEBUG, SWTEXT("Panic: Can't insert IP.\n"));
        __DATA->bPanic = _TRUE;
        return;
    }

    /* Append case */
    if (nIndex == *pnCount) {
        pRowTable[(*pnCount)++] = pItem;
        return;
    }

    /* Out-of-bounds */
    if (nIndex > *pnCount) {
        Log(SWLogger_DEBUG, SWTEXT("Panic: IP OOB insert.\n"));
        __DATA->bPanic = _TRUE;
        ET9Assert(nIndex < *pnCount);
        return;
    }

    /* Insert */
    _ET9ByteMove(&pRowTable[nIndex + 1], &pRowTable[nIndex], (*pnCount - nIndex) * sizeof(SWCIPTableRow *));

    pRowTable[nIndex] = pItem;
    ++(*pnCount);

#if __ET9A_DEBUG_MEM_SIZE
    {
        ET9INT *nMaxSize;

        if (pRowTable == __DATA->sBackEnd.pIPTable->m_aIPTable) {
            nMaxSize = &nMaxArraySize1;
        }
        else {
            nMaxSize = &nMaxArraySize2;
        }
        if (*pnCount > *nMaxSize) {
            *nMaxSize = *pnCount;
            if (*nMaxSize % 10 == 0) {
                Log(SWLogger_DEBUG, TEXT("Cap %d table now at %d.\n"), nCapacity, *nMaxSize);
            }
        }
    }
#endif
}


/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                     
 *
 *                                                                                               
 *                                                                    
 *                                                       
 *                                                       
 *                                                  
 *
 *             
 */

void ET9FARCALL SWCIPTableRow_append(
                       _ET9AWSPContext *pContext,
                       SWCIPTableRow **pRowTable,
                       ET9S16 *pnCount,
                       ET9S16 nCapacity,
                       SWCIPTableRow *pItem)
{
    /* Out of space */
    if (*pnCount >= nCapacity) {
        Log(SWLogger_DEBUG, SWTEXT("Panic: Can't append IP.\n"));
        __DATA->bPanic = _TRUE;
        return;
    }

    /* Append */
    pRowTable[(*pnCount)++] = pItem;

    ET9Assert(((*pnCount - 1) == pItem->m_IPIndex) || (__DATA->sBackEnd.pIPTable->m_aIPTable != pRowTable));

#if __ET9A_DEBUG_MEM_SIZE
    {
        ET9INT *nMaxSize;

        if (pRowTable == __DATA->sBackEnd.pIPTable->m_aIPTable) {
            nMaxSize = &nMaxArraySize1;
        }
        else {
            nMaxSize = &nMaxArraySize2;
        }
        if (*pnCount > *nMaxSize) {
            *nMaxSize = *pnCount;
            if (*nMaxSize % 10 == 0) {
                Log(SWLogger_DEBUG, TEXT("Cap %d table now at %d.\n"), nCapacity, *nMaxSize);
            }
        }
    }
#endif
}


/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                                      
 *
 *                                                                                               
 *                                                                    
 *                                                                   
 *                                                       
 *
 *             
 */

void ET9FARCALL SWCIPTableRow_erase(
                       _ET9AWSPContext *pContext,
                       SWCIPTableRow **pRowTable,
                       ET9S16 nIndex,
                       ET9S16 *pnCount)
{
    /* Out-of-bounds */
    if (nIndex >= *pnCount) {
        Log(SWLogger_DEBUG, SWTEXT("Panic: IP OOB erase.\n"));
        __DATA->bPanic = _TRUE;
        ET9Assert(nIndex < *pnCount);
        return;
    }

    /* Remove */
    _ET9ByteMove(&pRowTable[nIndex], &pRowTable[nIndex + 1], (*pnCount - nIndex - 1) * sizeof(SWCIPTableRow *));
    --(*pnCount);
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                     
 *
 *                                                             
 *                                                  
 *
 *             
 */

void ET9FARCALL SWFixedData_append(SWCIPTable *pThis, SWFixedData *pItem)
{
    /* Out of space */
    if (pThis->wPathPointCount >= _SWYPE_MAX_PATH_POINTS) {
        Log(SWLogger_DEBUG, SWTEXT("Panic: Fixed data path point overflow.\n"));
        _ET9AWSP_DATA->bPanic = _TRUE;
        return;
    }

    /* Append */
    pThis->m_aPathPoints[pThis->wPathPointCount++] = pItem;

#if __ET9A_DEBUG_MEM_SIZE
    {
        if (pThis->wPathPointCount > nMaxArraySize3) {
            nMaxArraySize3 = pThis->wPathPointCount;
            if (nMaxArraySize3 % 10 == 0) {
                Log(SWLogger_DEBUG, TEXT("Cap %d point array now at %d.\n"), _SWYPE_MAX_PATH_POINTS, nMaxArraySize3);
            }
        }
    }
#endif
}

/*============================================================================= */
/* Function:    SWCIPTable_GetFixedDataSize() */
/* Parameters:  searchLevel = If set to ThisSearchVal, this is a call from reProcessIPTable() */
/*              so get the fixed point data from the current data saved by SearchDB() */
/*              Otherwise, get the data from the Analyzer currently being collected and */
/*              processed. */
/* Returns:     Count of fixed point data */
/*============================================================================= */

SBYTE2 ET9FARCALL SWCIPTable_GetFixedDataSize(SWCIPTable *pThis, BYTE2 searchLevel)
{
    if (searchLevel == ThisSearchVal)
        return(_SWCSearchDB_GetZ1FixedDataSize(pThis->m_backend->m_pSearchDB));
    else
        return((SBYTE2)SWCIPAnalyzer_GetFixedDataSize(pThis->m_backend->pIPAnalyzer));
} /* SWCIPTable_GetFixedPoint() */

/*============================================================================= */
/* Function:    SWCIPTable_GetFixedPoint() */
/* Parameters:  searchLevel = If set to ThisSearchVal, this is a call from reProcessIPTable() */
/*              so get the fixed point data from the current data saved by SearchDB() */
/*              Otherwise, get the data from the Analyzer currently being collected and */
/*              processed. */
/*              fixedIndex - Index of fixed point for which data is to be obtained */
/* Returns:     Point coordinates of fixed point */
/*============================================================================= */

_SWPoint ET9FARCALL SWCIPTable_GetFixedPoint(SWCIPTable *pThis, SBYTE2 fixedIndex, BYTE2 searchLevel, SBYTE2 callNum)
{
    if ((searchLevel == ThisSearchVal) || (SWCIPAnalyzer_GetFixedDataSize(pThis->m_backend->pIPAnalyzer) < fixedIndex))
        return(_SWCSearchDB_GetZ1FixedPoint(pThis->m_backend->m_pSearchDB, fixedIndex));
    else
        return(SWCIPAnalyzer_GetFixedPoint(pThis->m_backend->pIPAnalyzer, fixedIndex, callNum));
} /* SWCIPTable_GetFixedPoint() */

/*============================================================================= */
/* Function:    SWCIPTable_GetFixedD2Sum() */
/* Parameters:  searchLevel = If set to ThisSearchVal, this is a call from reProcessIPTable() */
/*              so get the fixed point data from the current data saved by SearchDB() */
/*              Otherwise, get the data from the Analyzer currently being collected and */
/*              processed. */
/*              fixedIndex - Index of fixed point for which data is to be obtained */
/* Returns:     D2Sum field value of fixed point */
/*============================================================================= */

SBYTE4 ET9FARCALL SWCIPTable_GetFixedD2Sum(SWCIPTable *pThis, SBYTE2 fixedIndex, BYTE2 searchLevel)
{
    if ((searchLevel == ThisSearchVal) || (SWCIPAnalyzer_GetFixedDataSize(pThis->m_backend->pIPAnalyzer) < fixedIndex))
        return(_SWCSearchDB_GetZ1FixedD2Sum(pThis->m_backend->m_pSearchDB, fixedIndex));
    else
        return(SWCIPAnalyzer_GetFixedD2Sum(pThis->m_backend->pIPAnalyzer, fixedIndex));
} /* SWCIPTable_GetFixedD2Sum() */

/*============================================================================= */
/* Function:    SWCIPTable_SetIPDistances1() */
/* Parameters:  SWCIPTableRow *ipData - Pointer to row of IP in which data is to be stored */
/* Description: Calculate the actual distance of each key from the IP, and store the distance */
/*              calculated in the SWCIPTableRow object */
/*============================================================================= */

void ET9FARCALL SWCIPTable_SetIPDistances1(SWCIPTable *pThis, SWCIPTableRow *ipData)
{
    BYTE2       Distance;
    SBYTE2      keyIndex;
    _SWPoint    KeyLoc, IPLoc;
    ET9BOOL     bottomRow;
    SWDbm       *pDbm;

    if (!SWCIPTable_IPCalcNeeded(pThis, ipData, IP_DISTANCES1))
        return;

    bottomRow = (ipData->bottomRow);
    pDbm = pThis->m_backend->m_pDbm;
    for (keyIndex = 0; keyIndex < SWDbm_letterKeyCnt(pDbm); keyIndex++)
    {
        SWDbm_getKeyCenterScaled(pDbm, (BYTE1)keyIndex, &KeyLoc); /*lint !e613        // Get location of key */
        if (bottomRow && (KeyLoc.y >= pThis->m_backend->sScreenGeometry.bottomRowMidY))
            IPLoc = ipData->exitLoc;
        else
            IPLoc = ipData->m_IPLocation;
        Distance = _SWPoint_distance8(&KeyLoc, &IPLoc);     /* distance8() returns min value of 1 */
        ipData->m_IPDistance8[keyIndex] = Distance;
        ipData->m_IPAdjDistance8[keyIndex] = Distance;      /* Init to standard value (actual distance from IP). May be re-set to lower value below. */
                                                            /* May be over-written below for PenUp and Angle IPs */
    }
    ipData->m_RowFilled = (ipData->m_RowFilled | IP_DISTANCES1);
}   /* SWCIPTable_SetIPDistances1() */

/*============================================================================= */
/* Function:    SWCIPTable_SetIPDistance8() */
/* Parameters:  SWCIPTableRow *ipData - Pointer to row of IP in which data is to be stored */
/*              BYTE1 keyIndex - Index of key for which data is to be stored */
/* Returns:     SBYTE2 - Corresponding data stored in IP */
/* Description: Calculate the distance of the indicated key from the IP */
/*              indicated by the IPIndex parameter, and store the distance */
/*              calculated in the corresponding SWCIPTableRow object */
/*============================================================================= */

BYTE2 ET9FARCALL SWCIPTable_SetIPDistance8(SWCIPTable *pThis, SWCIPTableRow *ipData, SWCIPTableRow *ipData0, SWCIPTableRow *ipData2, BYTE1 keyIndex, BYTE2 searchLevel)
{
    BYTE2       Distance, SegDistance, SegDistance1, wDistance, wOvershootDistance, minWDistance;
    _SWPoint    KeyLoc, IPLoc;
    ET9BOOL     bottomRow;
    SWDbm       *pDbm;
    if (ipData == NULL)
        return(W_DISTANCE8_MAX);

    pDbm = pThis->m_backend->m_pDbm;
#if DEBUG_SHOW_RECALC_COUNTS
    pThis->IPDistanceCalcCount++;
#endif
    bottomRow = (ipData->bottomRow);

    /* Init m_IPAdjDistance8[] to standard value (actual distance from IP). May be re-set to lower value below for PenUp and Angle IPs. */
    if (SWCIPTable_IPCalcNeeded(pThis, ipData, IP_DISTANCES1))
        SWCIPTable_SetIPDistances1(pThis, ipData);
    if (ipData0 != NULL)
        SWCIPTable_SetSegDistances(pThis, ipData0, ipData, searchLevel);
    if (SWCIPTable_IPCalcNeeded(pThis, ipData2, IP_DISTANCES1))
        SWCIPTable_SetIPDistances1(pThis, ipData2);
    if (ipData2 != NULL)
        SWCIPTable_SetSegDistances(pThis, ipData, ipData2, searchLevel);
    minWDistance = MIN_IP_WDISTANCE8;
    SWDbm_getKeyCenterScaled(pDbm, keyIndex, &KeyLoc);    /*lint !e613        // Get location of key */
    if (KeyLoc.y < pThis->m_backend->sScreenGeometry.bottomRowMidY)
        bottomRow = _FALSE;
    if (!bottomRow)
        IPLoc = ipData->m_IPLocation;
    else
        IPLoc = ipData->exitLoc;

    Distance = ipData->m_IPDistance8[keyIndex];     /* distance8() returns min value of 1 */
    /* SavedCodeSegments/SearchDB.cpp/157 */

    if ((ipData->m_IPType == Exit) || (ipData->m_IPType == Entry))
    {
        ipData->m_IPWDistance8[keyIndex] = 0;
        return 0;
    }
#if  USE_DISTANCE2Y8_IP
    wDistance = _SWPoint_distance2y8(&pThis->m_backend->sScreenGeometry, &KeyLoc, &IPLoc);
#else
    wDistance = Distance;
#endif
    /* Allow overshoot for qualified IPs (PenUp, Angle) without overly penalizing score.  Adjust weighted distance according to closest */
    /*   point on segment (if closer) */
    if (ipData->m_IPFlags & OVERSHOOT_IP)
    {
        if (ipData->m_IPType == PenUp)
            minWDistance = MIN_PENUP_WDISTANCE8;
        SegDistance = SegDistance1 = ipData->m_SegmentDistance8[keyIndex];
        if (!DebugAssert(SegDistance1 != 0))
        {
            SegDistance1 = DISTANCE8_MAX;
        }
        if (SegDistance == DISTANCE8_MAX)
            SegDistance = 0;
        /* SavedCodeSegments/SearchDB.cpp/149 */
        if (ipData2 != NULL)
        {
            BYTE2 SegDistance2 = ipData2->m_SegmentDistance8[keyIndex];
            if (DebugAssert(SegDistance2 != 0) && SegDistance2 < SegDistance1)
            {
                SegDistance1 = SegDistance2;
            }
        }
        if (SegDistance1 == DISTANCE8_MAX)
            SegDistance1 = 0;
        /* SavedCodeSegments/SearchDB.cpp/150 */
        /* SavedCodeSegments/288 - Previous version of overshoot/undershoot adjustment */
#if ADJUST_FOR_OVERSHOOT

        /* Don't adjust distance unless path segment is close enough to key (and closer than IP itself) */
        if (SegDistance1 && (SegDistance1 < pThis->PenUpOvershootSegmentThreshold8[searchLevel]) && (SegDistance1 < wDistance) &&
            (ipData->m_IPPosIndex > ipData->m_LinePos[keyIndex]))
        {
            /* Test against less stringent "PenUp Overshoot" Segment threshold */
            if (Distance <= pThis->PenUpOvershootIPThreshold8[searchLevel])
            {       /* Set weighted and unweighted distances to weighted averages of the IP- and segment-distance values */
                /*wOvershootDistance = sw_max(((SegDistance1 + wDistance) / 2), MIN_IP_DISTANCE8_PENUP_OVERSHOOT); */
                /*wOvershootDistance = (SegDistance1 + (2 * wDistance)) / 3; */
                wOvershootDistance = (SegDistance1 + (3 * wDistance)) / 4;
                /* Use overshoot-adjusted PenUp weighted distance only if less than standard measurement */
                if (wOvershootDistance < wDistance)
                    wDistance = wOvershootDistance;
                if (SegDistance1 < Distance)
                {
                    /*ipData->m_IPAdjDistance8[keyIndex] = Distance = (SegDistance1 + (2 * Distance)) / 3; */
                    ipData->m_IPAdjDistance8[keyIndex] = Distance = (SegDistance1 + (3 * Distance)) / 4;
                }
            }
        }
#if ADJUST_FOR_UNDERSHOOT
        /* If path does not reach key, but the preceding path segment exceeds the required thresholds for length and straightness, then */
        /*   adjust distance from IP if path was heading "more or less" straight toward the key */
        else if ((!SegDistance || (SegDistance > Distance)) && (Distance < pThis->PenUpUndershootIPThreshold8[searchLevel]) && !bottomRow)
        {
            SBYTE4  keyVector1Change8 = -1;
            SBYTE4  keyVector2Change8 = -1;
            if ((ipData0 != NULL) && (ipData->m_SegmentLen8 >= MIN_PENUP_UNDERSHOOT_PATH_LENGTH8) && (ipData->m_fLengthRatio <= MAX_UNDERSHOOT_SEGMENT_RATIO))
            {
                /* Determine how close the key is to a straight-line extension of the final segment of the path */
                SBYTE2  pathIndex = ipData->m_FixedIndexIn - PENUP_UNDERSHOOT_PATH_INDEX_OFFSET;
                if (pathIndex > ipData0->m_FixedIndexOut)
                {
                    _SWPoint PathLoc = SWCIPTable_GetFixedPoint(pThis, pathIndex, searchLevel, 14);
                    _SWVector    pathVector = _SWVector(PathLoc, IPLoc);
                    _SWVector    keyVector = _SWVector(PathLoc, KeyLoc);
                    /* Scale the vector difference by the normalized length of the PathLoc-to-KeyLoc vector.  This */
                    /*   provides a reasonable approximation of the distance of the key from a straight-line extension of the path. */
                    keyVector1Change8 = ((SBYTE4)pathVector.slopeDifference(keyVector) * PathLoc.distance8(KeyLoc)) / NORM_VECTOR_LEN;
                }
            }
            if ((ipData2 != NULL) && (ipData2->m_SegmentLen8 >= MIN_PENUP_UNDERSHOOT_PATH_LENGTH8) && (ipData2->m_fLengthRatio <= MAX_UNDERSHOOT_SEGMENT_RATIO))
            {
                /* Determine how close the key is to a straight-line extension of the final segment of the path */
                SBYTE2  pathIndex = ipData->m_FixedIndexOut + PENUP_UNDERSHOOT_PATH_INDEX_OFFSET;
                if (pathIndex < ipData2->m_FixedIndexIn)
                {
                    _SWPoint PathLoc = SWCIPTable_GetFixedPoint(pThis, pathIndex, searchLevel, 15);
                    _SWVector    pathVector = _SWVector(PathLoc, IPLoc);
                    _SWVector    keyVector = _SWVector(PathLoc, KeyLoc);
                    /* Scale the vector difference by the normalized length of the PathLoc-to-KeyLoc vector.  This */
                    /*   provides a reasonable approximation of the distance of the key from a straight-line extension of the path. */
                    keyVector2Change8 = ((SBYTE4)pathVector.slopeDifference(keyVector) * PathLoc.distance8(KeyLoc)) / NORM_VECTOR_LEN;
                    if (keyVector1Change8 >= 0)
                        keyVector1Change8 = sw_min(keyVector1Change8, keyVector2Change8);
                    else
                        keyVector1Change8 = keyVector2Change8;
                }
            }
            if ((keyVector1Change8 >= 0) && (keyVector1Change8 < pThis->PenUpUndershootSegmentThreshold8[searchLevel]))
            {
                /*wUndershootDistance = (keyVector1Change8 + wDistance) / 2; */
                /*wUndershootDistance = (keyVector1Change8 + (2 * wDistance)) / 3; */
                BYTE2 wUndershootDistance = (keyVector1Change8 + (3 * wDistance)) / 4;
                /* Use undershoot-adjusted PenUp weighted distance only if less than standard measurement */
                if (wUndershootDistance < wDistance)
                    wDistance = wUndershootDistance;
                if (keyVector1Change8 < Distance)
                {
                    /*ipData->m_IPAdjDistance8[keyIndex] = Distance = (keyVector1Change8 + Distance) / 2; */
                    /*ipData->m_IPAdjDistance8[keyIndex] = Distance = (keyVector1Change8 + (2 * Distance)) / 3; */
                    ipData->m_IPAdjDistance8[keyIndex] = Distance = (keyVector1Change8 + (3 * Distance)) / 4;
                }
            }
        }
#endif
#endif
        /* SavedCodeSegments/IPTable.cpp/34 */
    }
    if ((Distance > pThis->IPthreshold8[ipData->m_IPType][searchLevel]) || (ipData->m_IPType == PathDivision))         /* No weighted distance for PathDivision */
        ipData->m_IPWDistance8[keyIndex] = W_DISTANCE8_MAX;
    else
    {
#if FORCE_MINIMUM_SEGMENT_DISTANCE
        /* If less than minimum distance, then set as average of actual distance and minimum value to */
        /* reduce the influence of paths that happen to coincide closely with keys of low-freq words */
        if (wDistance < minWDistance)
            wDistance = (wDistance + minWDistance) / 2;
#endif
        wDistance = (BYTE2)(long)(wDistance * ipData->m_fIPWeight);
        if (wDistance < 1)
            wDistance = 1;
        ipData->m_IPWDistance8[keyIndex] = wDistance;
    }
    /* SavedCodeSegments/SearchDB.cpp/155 */
    return wDistance;
} /* SWCIPTable_SetIPDistance8() */



/*============================================================================= */
/* Function:    SWCIPTable_SetSegDistances() */
/* Parameters:  SWCIPTableRow *ipData1 - Pointer to row of IP Table in which data is to be stored */
/*              SWCIPTableRow *ipData0 - Pointer to preceding row of IP Table */
/* Description: Calculate the actual distance of each key from the IP, and store the distance */
/*              calculated in the SWCIPTableRow object */
/*============================================================================= */

void ET9FARCALL SWCIPTable_SetSegDistances(SWCIPTable *pThis, SWCIPTableRow *ipData0, SWCIPTableRow *ipData1, BYTE2 searchLevel)
{
    BYTE1       calcFlag, keyIndex;
    SWDbm       *pDbm;

    if ((ipData0 == NULL) || (ipData1 == NULL))
        return;
    pDbm = pThis->m_backend->m_pDbm;
    /* Nothing to do if still at initial search level */
    if (searchLevel == SearchLevelMaxVal)
        calcFlag = SEG_DISTANCES1;
    else
        calcFlag = SEG_DISTANCES2;
    if (!SWCIPTable_IPCalcNeeded(pThis, ipData1, calcFlag))
        return;
    if (SWCIPTable_IPCalcNeeded(pThis, ipData0, IP_DISTANCES1))
        SWCIPTable_SetIPDistances1(pThis, ipData0);
    if (SWCIPTable_IPCalcNeeded(pThis, ipData1, IP_DISTANCES1))
        SWCIPTable_SetIPDistances1(pThis, ipData1);

    for (keyIndex = 0; keyIndex < SWDbm_letterKeyCnt(pDbm); keyIndex++)
        SWCIPTable_SetSegmentDistance8(pThis, ipData0, ipData1, keyIndex, searchLevel);

    if (searchLevel != SearchLevelMaxVal)
        calcFlag = (SEG_DISTANCES1 | SEG_DISTANCES2);
    ipData1->m_RowFilled = (ipData1->m_RowFilled | calcFlag);
}   /* SWCIPTable_SetSegDistances() */


#if DEBUG_IPT
static BYTE1 testKey1 = 14;
static BYTE1 testKey2 = 14;
static SBYTE2 ipIndexTest1 = 2;
static SBYTE2 ipIndexTest2 = 2;
#endif
/*============================================================================= */
/* Function:    SWCIPTable_SetSegmentDistance8() */
/* Parameters:  SWCIPTableRow *ipData1 - Pointer to row of IP Table in which data is to be stored */
/*              SWCIPTableRow *ipData0 - Pointer to preceding row of IP Table */
/*              BYTE1 keyIndex - Index of key for which data is to be stored */
/* Returns:     SBYTE2 - Corresponding data stored in IP */
/* Description: Calculate the distance of the indicated key from the IP */
/*              indicated by the IPIndex parameter, and store the distance */
/*              calculated in the corresponding SWCIPTableRow object */
/*============================================================================= */

void ET9FARCALL SWCIPTable_SetSegmentDistance8(SWCIPTable *pThis, SWCIPTableRow *ipData0, SWCIPTableRow *ipData1, BYTE1 keyIndex, BYTE2 searchLevel)
{
    BYTE2       mDistance, mDistance1, mWDistance;
    float       segWeight;
    SBYTE4      mProjectedIndexAdvance, xDif, yDif, XYSumSum[4], MinXYSum, MaxXYSum, XYSum;
    SBYTE2      mProjectedIndex, index, pathIndex, closePathIndex, lastCloseIndex, D2SumLocalMaxPos;
    SBYTE2      closePointCount[4], midPointIndex[4];
    SBYTE2      keyIP0, keyIP1, keyIPSum;
    SBYTE2      nFixedCount, fixedSearchLimit0, fixedSearchLimit1, fixedMatchLimit0, fixedMatchLimit1;
    SBYTE2      closePathLimit0, closePathLimit1;
    _SWPoint    mProjectedPoint, mProjectedPoint1, mKeyLoc;
    ET9BOOL     foundprojectedPoint;
    ET9BOOL     exhaustiveSearch = ((ipData0->m_MergeCount > 1) || (ipData1->m_MergeCount > 1));
    ET9BOOL     bottomRow0 = ipData0->bottomRow;
    ET9BOOL     bottomRow1 = ipData1->bottomRow;
    ET9BOOL     closer = _TRUE;          /* Init assuming local search is needed below */
    ET9BOOL     found = _FALSE;
    ET9BOOL     pointIPSumOK;
    BYTE2       DistanceInitial;
    BYTE2       wDistanceInitial;

    SWDbm*  pDbm = pThis->m_backend->m_pDbm;

#if DEBUG_IPT
    if (((ipData1->m_IPIndex == ipIndexTest1) && (keyIndex == testKey1)) ||
        ((ipData1->m_IPIndex == ipIndexTest2) && (keyIndex == testKey1)) ||
        ((ipData1->m_IPIndex == ipIndexTest1) && (keyIndex == testKey2)) ||
        ((ipData1->m_IPIndex == ipIndexTest2) && (keyIndex == testKey2)))
    {ET9BOOL breakHere = _TRUE; (void)breakHere;}
#endif

#if DEBUG_SHOW_RECALC_COUNTS
    pThis->SegDistanceCalcCount++;
#endif
    keyIP0 = ipData0->m_IPDistance8[keyIndex];
    keyIP1 = ipData1->m_IPDistance8[keyIndex];
#ifdef DEBUG_CASE_520
    AlwaysAssert(keyIP0 && keyIP1);
#endif
    pointIPSumOK = (ET9BOOL)((keyIP0 != 0) && (keyIP1 != 0) && ((keyIP0 + keyIP1) < (ipData1->m_SegmentLen8 + pThis->PointIPSumLimit[searchLevel])));
    DistanceInitial = ipData1->m_SegmentDistance8[keyIndex];
    wDistanceInitial = ipData1->m_SegmentWDistance8[keyIndex];
    /* SavedCodeSegments/SearchDB.cpp/151 */
    /* See if we can predict the outcome if we are recalculating a previously calculated distance using new thresholds */
    if (DistanceInitial && pointIPSumOK && (ipData1->m_fLengthRatio < MAX_CURVATURE_RATIO) && !exhaustiveSearch)
    {
        /* If we are still under (or still over) the new threshold, just return */
        if ( ((DistanceInitial < pThis->SegmentThreshold8[searchLevel]) && (wDistanceInitial < DISTANCE8_MAX)) ||
             ((DistanceInitial >= pThis->SegmentThreshold8[searchLevel]) && (wDistanceInitial == DISTANCE8_MAX)) )
             return;
        /* If we are now over the new threshold, set the weeighted distance to DISTANCE8_MAX and return */
        if ((DistanceInitial >= pThis->SegmentThreshold8[searchLevel]) && (wDistanceInitial < DISTANCE8_MAX))
        {
            ipData1->m_SegmentWDistance8[keyIndex] = DISTANCE8_MAX;
            return;
        }
    }

    mDistance = DISTANCE8_MAX;                  /* Assume no match until we find otherwise */
    mWDistance = W_DISTANCE8_MAX;
    mProjectedIndex = D2SumLocalMaxPos = IPT_NO_MATCH;
    segWeight = 0.0;
    /* For PathDivision IPs, we want a key near the IP itself to match the preceding segment or the following */
    /*   segment, but NOT BOTH in the same local neighborhood of the path.  Thus, for PathDivision IPs, we */
    /*   search slightly beyond the IP to be sure we find the best local match location, but only allow the match */
    /*   when it is in one of two disjoint local regions */
    fixedSearchLimit0 = fixedMatchLimit0 = ipData0->m_FixedIndexOut;
    closePathLimit0 = closePathLimit1 = 0;          /* Used as flag to not adjust closest path location near PathDivision IP */
    if ((ipData0->m_IPType != Entry) && (ipData0->m_IPType != PathDivision))
    {
        fixedSearchLimit0++;                /* Keep segment match at least 1 interval from IP location */
        fixedMatchLimit0++;
    }
    /* For pathDivision IP, match up through and including FixedIndexIn, but excluding FixedIndexOut.  Search far enough back so that, */
    /*   if in fact closest match falls in preceding segment, we identify that fact and don't match in current segment */
    else if (ipData0->m_IPType == PathDivision)
    {
        fixedSearchLimit0 -= 4;
        if (fixedSearchLimit0 < 1)
            fixedSearchLimit0 = 1;
        fixedMatchLimit0++;
        closePathLimit0 = fixedMatchLimit0;
    }
    if (!AlwaysAssert((fixedSearchLimit0 >= 1) && (fixedMatchLimit0 >= 1)))
    {
        fixedSearchLimit0 = fixedMatchLimit0 = 1;
        ipData0->m_FixedIndexOut = 0;
    }
    fixedSearchLimit1 = fixedMatchLimit1 = ipData1->m_FixedIndexIn;
    if ((ipData1->m_IPType != Exit) && (ipData1->m_IPType != PathDivision))
    {
        fixedSearchLimit1--;                        /* Keep segment match at least 1 interval from IP location */
        fixedMatchLimit1--;
    }
    /* For pathDivision IP, match up through and including FixedIndexOut, but excluding FixedIndexIn.  Search far enough back so that, */
    /*   if in fact closest match falls in preceding segment, we identify that fact and don't match in current segment */
    else if (ipData1->m_IPType == PathDivision)
    {
        fixedSearchLimit1 += 4;
        if (fixedSearchLimit1 >= SWCIPTable_GetFixedDataSize(pThis, searchLevel))
            fixedSearchLimit1 = SWCIPTable_GetFixedDataSize(pThis, searchLevel) - 1;
        closePathLimit1 = fixedMatchLimit1;
    }
    if ((fixedSearchLimit1 < 0) || (fixedMatchLimit1 < 0))
    {
        fixedSearchLimit1 = 0;
        fixedMatchLimit1 = 0;
    }
    if (ipData1->m_IPType != Entry)             /* Segment preceding Entry IP is outside of keyboard. Leave all values set to invalid. */
    {
        SWDbm_getKeyCenterScaled(pDbm, keyIndex, &mKeyLoc);   /*lint !e613        // Get location of key */
        if (!keyIP0 || bottomRow0)  /* m_IPDistance8[] is not actual distance if bottomRowX is _TRUE */
        {
            keyIP0 = _SWPoint_distance8(&mKeyLoc, &ipData0->m_IPLocation);      /* distance8() returns min value of 1 */
            if (!bottomRow0)        /* Only write to IPData array if this was an uninitialized, non-bottom-row IP */
                ipData0->m_IPDistance8[keyIndex] = keyIP0;
        }
        if (!keyIP1 || bottomRow1)
        {
            keyIP1 = _SWPoint_distance8(&mKeyLoc, &ipData1->m_IPLocation);      /* distance8() returns min value of 1 */
            if (!bottomRow1)        /* Only write to IPData array if this was an uninitialized, non-bottom-row IP */
                ipData1->m_IPDistance8[keyIndex] = keyIP1;
        }
        keyIPSum = keyIP0 + keyIP1;                     /* Sum of (actual) distances to key from each IP */
        foundprojectedPoint = _FALSE;
        /* If curvature of segment exceeds threshold, or if either IP is a merged IP, then heuristic below may fail. */
        /* Scan segment to estimate closest point of approach */
        if ((ipData1->m_fLengthRatio > MAX_CURVATURE_RATIO) || exhaustiveSearch || !pointIPSumOK)
        {
            /* Set threshold to squared distance of current matching threshold */
            MaxXYSum = (pThis->SegmentThreshold8[searchLevel] * pThis->SegmentThreshold8[searchLevel]) / 64;
            MinXYSum = MaxXYSum + 1;    /* Smallest value that is also smaller than matching threshold */
            for (index = 0; index < 4; index++)
            {
                closePointCount[index] = 0;
                XYSumSum[index] = 0;
            }
            pathIndex = -1;
            closePathIndex = -1;
            lastCloseIndex = -2;
            for (index = fixedSearchLimit0; index <= fixedSearchLimit1; index++)
            {
                mProjectedPoint1 = SWCIPTable_GetFixedPoint(pThis, index, searchLevel, 16);
                xDif = (SBYTE4)(mProjectedPoint1.x - mKeyLoc.x);
                yDif = (SBYTE4)(mProjectedPoint1.y - mKeyLoc.y);
                XYSum = (xDif * xDif) + (yDif * yDif);
                if (XYSum < MaxXYSum)
                {
                    if (index > (lastCloseIndex + 2))   /* This is first close point on a new path segment (allows up to 1 out-of-range point) */
                    {
                        if (pathIndex < 3)
                            pathIndex++;
                        else        /* Just make sure best match is preserved.  Copy to slot 0 */
                        {
                            if (closePathIndex > 0)
                            {
                                XYSumSum[0] = XYSumSum[closePathIndex];
                                closePointCount[0] = closePointCount[closePathIndex];
                                midPointIndex[0] = midPointIndex[closePathIndex];
                                closePathIndex = 0;
                            }
                            {
                                SBYTE2 resetIndex;
                                for (resetIndex = 1; resetIndex < 4; resetIndex++)
                                {
                                    closePointCount[resetIndex] = 0;
                                    XYSumSum[resetIndex] = 0;
                                }
                            }
                            pathIndex = 1;
                        }
                    }
                    lastCloseIndex = index;
                    if ((pathIndex < 0) || (pathIndex >= 4))
                        pathIndex = 0;
                    closePointCount[pathIndex]++;           /* Count number of points within threshold to detect path that circles a key */
                    midPointIndex[pathIndex] = lastCloseIndex - (closePointCount[pathIndex] / 2);
                    XYSumSum[pathIndex] += XYSum;
                    if (XYSum < MinXYSum)
                    {
                        MinXYSum = XYSum;
                        mProjectedIndex = index;
                        mProjectedPoint = mProjectedPoint1;
                        closePathIndex = pathIndex;
                        foundprojectedPoint = _TRUE;
                    }
                }
            }
            if (foundprojectedPoint)
            {
                mDistance = _SWPoint_distance8(&mKeyLoc, &mProjectedPoint);         /* distance8() returns min value of 1 */
                /* Don't attempt heuristic to identify the midpoint of a path segment that loops around the key if the identified */
                /* point is too close (within one key height) to a PathDivision IP, since a PathDivision IP places artificial  */
                /* limits on finding the relevant looping portion (i.e. just default to using the closest identified point).  */
                if ((closePathIndex >= 0) && (closePointCount[closePathIndex] > 0) &&
                    (!closePathLimit0 || (absGeo(mProjectedIndex - closePathLimit0) > PATH_DIVISION_SEG_MATCH_MARGIN)) &&
                    (!closePathLimit1 || (absGeo(closePathLimit1 - mProjectedIndex) > PATH_DIVISION_SEG_MATCH_MARGIN)))
                {
                    float   avRadius = _ET9sqrt_f((float)XYSumSum[closePathIndex] / (float)closePointCount[closePathIndex]);
                    /* Approximate circumference of a path circle around the key (assuming it DOES circle around the key). */
                    float   circumference = (float)2.0 * avRadius * (float)3.141592;
                    float   closePathLength = (float)((closePointCount[closePathIndex] - 1) * Z1_FIXED_INTERVAL);
                    float   portion = closePathLength / circumference;
                    if (portion > LOOPING_PATH_PORTION_THRESHOLD1)
                    {
                        mProjectedIndex = midPointIndex[closePathIndex];
                        if (portion > LOOPING_PATH_PORTION_THRESHOLD3)
                            mDistance = (BYTE2)(long)((float)mDistance / (portion * LOOPING_PATH_PORTION_FACTOR3));
                        else if (portion > LOOPING_PATH_PORTION_THRESHOLD2)
                            mDistance = (BYTE2)(long)((float)mDistance / (portion * LOOPING_PATH_PORTION_FACTOR2));
                        else
                            mDistance = (BYTE2)(long)((float)mDistance / (portion * LOOPING_PATH_PORTION_FACTOR1));
                    }
                }
            }
            closer = _FALSE; /* Init so that no local search is performed below, since exhaustive search has already been done */
            found = _TRUE;
        }
        /* Sum cannot exceed segment length by more than PointIPSumLimit */
        else if (pointIPSumOK)
        {
            nFixedCount = ipData1->m_FixedIndexIn - ipData0->m_FixedIndexOut;  /* Number of Fixed intervals along IP path segment */
            /* Use proportion of of distances of key to each IP as first estimate of matching position */
            mProjectedIndexAdvance = (nFixedCount * keyIP0) + (keyIPSum >> 1);
            mProjectedIndexAdvance /= keyIPSum;
            mProjectedIndex = ipData0->m_FixedIndexOut + (SBYTE2)mProjectedIndexAdvance;
            mProjectedPoint = SWCIPTable_GetFixedPoint(pThis, mProjectedIndex, searchLevel, 17);
            foundprojectedPoint = _TRUE;
            closer = _TRUE;          /* Init so that local search is performed below */
            found = _FALSE;
        }
        if (foundprojectedPoint)
        {
            if (!found)
            {
                mDistance = _SWPoint_distance8(&mKeyLoc, &mProjectedPoint);         /* distance8() returns min value of 1 */
            }
            for (index = (mProjectedIndex + 1); closer && (index <= fixedSearchLimit1); index++)
            {
                mProjectedPoint1 = SWCIPTable_GetFixedPoint(pThis, index, searchLevel, 18);
                mDistance1 = _SWPoint_distance8(&mKeyLoc, &mProjectedPoint1);           /* distance8() returns min value of 1 */
                if (mDistance1 <= mDistance)
                {
                    mDistance = mDistance1;
                    mProjectedPoint = mProjectedPoint1;
                    mProjectedIndex = index;
                    found = _TRUE;
                }
                else
                    closer = _FALSE;
            }
            if (!found && (mProjectedIndex > fixedSearchLimit0))            /* Check other direction if nothing closer found */
            {
                closer = _TRUE;
                for (index = (mProjectedIndex - 1); closer && (index >= fixedMatchLimit0); index--)
                {
                    mProjectedPoint1 = SWCIPTable_GetFixedPoint(pThis, index, searchLevel, 19);
                    mDistance1 = _SWPoint_distance8(&mKeyLoc, &mProjectedPoint1);           /* distance8() returns min value of 1 */
                    if (mDistance1 <= mDistance)
                    {
                        mDistance = mDistance1;
                        mProjectedPoint = mProjectedPoint1;
                        mProjectedIndex = index;
                    }
                    else
                        closer = _FALSE;
                }
            }
            /* No match if distance exceeds threshold, nor if key is closer to either IP than to segment itself */
            if ((mDistance < pThis->SegmentThreshold8[searchLevel]) && (mProjectedIndex >= fixedMatchLimit0) && (mProjectedIndex <= fixedMatchLimit1))
            {
                /* Constrain position where D2Sum is tested to be no closer than Z1_DIFFERENCE_OFFSET */
                /*   to the surrounding IPs */
                SBYTE2 D2TestPos = mProjectedIndex;
                SBYTE2 lowBound = fixedMatchLimit0 + (Z1_DIFFERENCE_OFFSET - 1);
                SBYTE2 highBound = fixedMatchLimit1 - (Z1_DIFFERENCE_OFFSET - 1);
                SBYTE4 D2SumLocalMax;
                ET9BOOL adjustLocalMaxPos = _FALSE;
                float D2Factor;
                /* Init now in case test fails, or all D2Sum values are 0 */
                D2SumLocalMaxPos = mProjectedIndex;
                D2SumLocalMax = SWCIPTable_GetFixedD2Sum(pThis, D2SumLocalMaxPos, searchLevel);
                if ((D2TestPos >= lowBound) && (D2TestPos <= highBound))
                {
                    /* Allow for "overshoot" when matching key to segment.  Measure distance from */
                    /* closest segment point, but look for higher values of D2Sum in a local neighborhood */
                    /* extending slightly past the matched point */
                    SBYTE2 localLimit1 = D2TestPos - Z1_SEGMENT_MATCH_D2SUM_LIMIT;
                    SBYTE2 localLimit2 = D2TestPos + Z1_SEGMENT_MATCH_D2SUM_LIMIT;
                    if (localLimit1 < lowBound)
                        localLimit1 = lowBound;
                    if (localLimit2 > highBound)
                        localLimit2 = highBound;
                    {
                        SBYTE2 i;
                        for (i = localLimit1; i <= localLimit2; i++)
                        {
                            SBYTE4 D2Check = SWCIPTable_GetFixedD2Sum(pThis, i, searchLevel);
                            if (D2Check > D2SumLocalMax)
                            {
                                D2SumLocalMax = D2Check;
                                D2SumLocalMaxPos = i;
                            }
                        }
                    }
                }
                /* Make sure D2SumLocalMaxPos does not collide with either IP (for KeysMatchPoints(...Loc...) testing) */
                if (D2SumLocalMaxPos == ipData0->m_FixedIndexOut)
                {
                    D2SumLocalMaxPos++;
                    adjustLocalMaxPos = _TRUE;
                }
                else if (D2SumLocalMaxPos == ipData1->m_FixedIndexIn)
                {
                    D2SumLocalMaxPos--;
                    adjustLocalMaxPos = _TRUE;
                }
                if (adjustLocalMaxPos)
                    D2SumLocalMax = SWCIPTable_GetFixedD2Sum(pThis, D2SumLocalMaxPos, searchLevel);
                /* Weight distance result by ratio of the D2Sum value at the matched path point to the D2Sum threshold */
                /*   for an Angle IP, scaled between the minimum weight for a Segment match and for an Angle IP */
                D2Factor = (float)D2SumLocalMax / (float)DIF2_ANGLE_THRESHOLD;
                D2Factor *= ipData1->m_fLengthFactor;
                segWeight = (float)((D2Factor * (AngleWeight - SegmentWeight)) + SegmentWeight);
                if (segWeight > AngleWeight)
                    segWeight = AngleWeight;
                ipData1->m_SegmentWeight[keyIndex] = segWeight;
#if FORCE_MINIMUM_SEGMENT_DISTANCE
                /* If actual distance less than minimum distance, use average of actual distance and minimum */
                if (mDistance < MIN_SEGMENT_WDISTANCE8)
                    mDistance = (mDistance + MIN_SEGMENT_WDISTANCE8) / 2;
#endif
#if USE_DISTANCE2Y8_SEGMENT
                /* mDistance may have been adjusted above; limit to twice adjusted value */
                mWDistance = sw_min(_SWPoint_distance2y8(&pThis->m_backend->sScreenGeometry, &mKeyLoc, &mProjectedPoint), (2 * mDistance));
#if FORCE_MINIMUM_SEGMENT_DISTANCE
                /* If actual distance less than minimum distance, use average of actual distance and minimum */
                if (mWDistance < MIN_SEGMENT_WDISTANCE8)
                    mWDistance = (mWDistance + MIN_SEGMENT_WDISTANCE8) / 2;
#endif
#else
                mWDistance = mDistance;
#endif
                mWDistance = (BYTE2)((float)mWDistance * segWeight);
                if (mWDistance == 0)
                    mWDistance = 1;         /* Minimum weighted distance is never less than 1 (to simplify testing) */
                if (mDistance == 0)
                    mDistance = 1;          /* Minimum actual distance is never less than 1 (to simplify testing) */
            }
            else
            {   /* If possible match turns out to be invalid, reset all flags to consistent invalid state */
                mWDistance = W_DISTANCE8_MAX;
                mDistance = DISTANCE8_MAX;
                mProjectedIndex = D2SumLocalMaxPos = IPT_NO_MATCH;
                segWeight = 0.0;
            }
        }
        /* SavedCodeSegments/SearchDB.cpp/147 */
    }
#if DEBUG_IPT
    if (((ipData1->m_IPIndex == ipIndexTest1) && (keyIndex == testKey1)) || ((ipData1->m_IPIndex == ipIndexTest2) && (keyIndex == testKey2)))
        {ET9BOOL breakHere = _TRUE; (void)breakHere;}
    if ((mDistance != DISTANCE8_MAX) && (mWDistance == DISTANCE8_MAX) && (mProjectedIndex != IPT_NO_MATCH))
    {ET9BOOL breakHere = (mProjectedIndex >= fixedMatchLimit0) && (mProjectedIndex <= fixedMatchLimit1); (void)breakHere;}
#endif
    /* SavedCodeSegments/SearchDB.cpp/152 */
    ipData1->m_SegmentDistance8[keyIndex] = mDistance;
    ipData1->m_SegmentWDistance8[keyIndex] = mWDistance;
    ipData1->m_LinePos[keyIndex] = mProjectedIndex;
    ipData1->m_D2LocalMaxPos[keyIndex] = D2SumLocalMaxPos;
    ipData1->m_SegmentWeight[keyIndex] = segWeight;
    return;
} /* SWCIPTable_SetSegmentDistance8() */



/*============================================================================= */
/* Function:    SWCIPTable_PathExitsKeyboard() */
/* Parameters:  BYTE2 fixedIndex1, fixedIndex2 - Indices of fixed points */
/* Returns:     SBYTE2 - X-coordinates of where path exits and re-enters keyboard between the */
/*              two fixed points (always non-zero), or 0 if no exit-entry between */
/*              the points. */
/* Description: Determine whether the path exits the keyboard along the specified */
/*              path segment. */
/*============================================================================= */

SBYTE2 ET9FARCALL SWCIPTable_PathExitsKeyboard(SWCIPTable *pThis, SBYTE2 fixedIndex1, SBYTE2 fixedIndex2, SBYTE2 *exitXpos)
{
    SBYTE2 i;
    for (i = 0; i < pThis->m_ShiftLocCount; i++)
    {
        if ((fixedIndex1 <= pThis->m_ShiftPos[i]) && (pThis->m_ShiftPos[i] <= fixedIndex2))
        {
            *exitXpos = pThis->m_ExitXPos[i];
            return(pThis->m_EntryXPos[i]);
        }
    }
    *exitXpos = 0;
    return 0;
} /* SWCIPTable_PathExitsKeyboard */

/*============================================================================= */
/* Function:    SWCIPTable_DoProcessTableRows() */
/* Parameters:  None */
/* Returns:     void */
/* Description: New table data has been added. Process what there is to process. */
/*============================================================================= */

void ET9FARCALL SWCIPTable_DoProcessTableRows(SWCIPTable *pThis)
{
    SWCIPTableRow **pIPTable = SWCIPTable_GetIPTableArray(pThis);                /* Get pointer to mutex-protected IP Table array */
    ET9UINT i;

    for (i = 0; i < (ET9UINT)pThis->wIPCount; ++i)
    {
        if (SWCIPTable_IPCalcNeeded(pThis, pIPTable[i], ADJ_IP_DISTANCES1))
        {
            SWCIPTable_ProcessIPTableRow(pThis, (SBYTE2) i);                              /* process any unfinished IP Table data item */

            pIPTable = SWCIPTable_GetIPTableArray(pThis);
        }
    }
} /* SWCIPTable_DoProcessTableRows */


/*============================================================================= */
/* Function:    SWCIPTable_ProcessIPTableRow() */
/* Parameters:  SBYTE2 IPIndex - index of single IP Table list data item to be processed */
/* Description: Process a single IP Table list data item. */
/*============================================================================= */

void ET9FARCALL SWCIPTable_ProcessIPTableRow(SWCIPTable *pThis, SBYTE2 IPIndex)
{
    SWDbm*  pDbm = pThis->m_backend->m_pDbm;

    SWCIPTableRow *ipData0, *ipData1, *ipData2, *ipExit;
    SBYTE2      exitIndex, tableSize;
    BYTE1       keyIndex;
    BYTE1       maxKeyCount = SWDbm_letterKeyCnt(pDbm);
    ET9BOOL     reCalc;

    SWCIPTableRow **pIPTable = SWCIPTable_GetIPTableArray(pThis);

    /* The IP table processing loop may attempt to process an IP that was originally added, but which */
    /*   was subsequently deleted by another thread. If so, just return. */
    tableSize = pThis->wIPCount;
    if ((IPIndex < 0) || (IPIndex >= tableSize))
    {
        if (IPIndex < 0)
        {
            _ET9ASW_Trace(SWTracer::TT_CIPTable, (BYTE2)IPIndex, StrToBYTE4((Str)TEXT("BADTBH")));
            _ET9ASW_Trace(SWTracer::TT_CIPTable, tableSize, StrToBYTE4((Str)TEXT("CURTBH")));
        }
        return;
    }
    ipData1 = pIPTable[IPIndex];

    if (SWCIPTable_IPCalcNeeded(pThis, ipData1, IP_DISTANCES1))
        SWCIPTable_SetIPDistances1(pThis, ipData1);
    /* Unless this is already the final (PenUp) IP, wait until following IP is also available for SetIPDistance8() calls */
    if ((IPIndex == (tableSize - 1)) && (ipData1->m_IPType != PenUp) && (ipData1->m_IPType != Tap) && (ipData1->m_IPType != SoftUp))
    {
        return;
    }
    if (IPIndex < (tableSize - 1))
    {
        ipData2 = pIPTable[IPIndex + 1];
        if (SWCIPTable_IPCalcNeeded(pThis, ipData2, IP_DISTANCES1))
            SWCIPTable_SetIPDistances1(pThis, ipData2);
    }
    else
        ipData2 = NULL;
    ipExit = NULL;
    if (IPIndex >= 1)
    {
        ipData0 = pIPTable[IPIndex - 1];
        exitIndex = ipData1->m_exitIndex;
        if (ipData0->m_IPType == Entry)
        {
            SBYTE2 exitIndexChk = ipData0->m_IPIndex - 1;
            if ((exitIndex >= 0) && (exitIndexChk == exitIndex) && (exitIndex < (ET9INT)pThis->wIPCount))
            {
                ipExit = pIPTable[exitIndex];
                if (ipExit->m_IPType != Exit)
                {
                    Log(SWLogger_INFO, SWTEXT("Swype Event: %d %d\r\n"), 408,(int)ipExit->m_IPType)
                    ipExit = NULL;
                }
            }
            else
            {
#if DEBUG_IPT
                SWCIPTable_dumpIPs(pThis, &pThis->m_aIPTable, &pThis->m_aIPTable2, 16, NULL);
#endif
                Log(SWLogger_INFO, SWTEXT("Swype Event: %d\r\n"), 409)
            }
        }
    }
    else
    {
        AlwaysAssert((ipData1->m_IPType == PenDown) || (ipData1->m_IPType == Tap));
        ipData0 = NULL;
    }
    reCalc = SWCIPTable_IPCalcNeeded(pThis, ipData1, ADJ_IP_DISTANCES1);

    if (ipData0 != NULL)
    {
        SWCIPTable_SetSegDistances(pThis, ipData0, ipData1, SearchLevelMaxVal);
    }
    else
    {
        AlwaysAssert((ipData1->m_IPType == PenDown) || (ipData1->m_IPType == Tap));
        for (keyIndex = 0; keyIndex < maxKeyCount; keyIndex++)
        {
            ipData1->m_SegmentDistance8[keyIndex] = DISTANCE8_MAX;
            ipData1->m_SegmentWDistance8[keyIndex] = DISTANCE8_MAX;
            ipData1->m_LinePos[keyIndex] = IPT_NO_MATCH;
            ipData1->m_D2LocalMaxPos[keyIndex] = IPT_NO_MATCH;
        }
        ipData1->m_RowFilled = (ipData1->m_RowFilled | SEG_DISTANCES1);
    }
    if (ipData1->m_IPType != PathDivision)          /* No IP distances needed for PathDivision IPs */
    {
        for (keyIndex = 0; keyIndex < maxKeyCount; keyIndex++)
        {
            if (!ipData1->m_IPDistance8[keyIndex] || reCalc)        /* Zero value means it has not yet been set */
                SWCIPTable_SetIPDistance8(pThis, ipData1, ipData0, ipData2, keyIndex, SearchLevelMaxVal);
        }
    }
    ipData1->m_RowFilled = (ipData1->m_RowFilled | ADJ_IP_DISTANCES1);
    AlwaysAssert(!SWCIPTable_IPCalcNeeded(pThis, ipData1, (INITIAL_DISTANCES | ADJ_IP_DISTANCES1)));

    /* IP data ready to display? */
    if ((ipData1->m_IPType == PenUp) || (ipData1->m_IPType == SoftUp) || (ipData1->m_IPType == Tap))
    {
        SWCIPTable_SetIPTableAnalyzedFlag(pThis, _TRUE);   /* Set Analyzed flag when table is free */
    }
    /* SavedCodeSegments/SearchDB.cpp/108 */
}   /* SWCIPTable_ProcessIPTableRow() */

/* test y-coordinates for two points to determine whether they are in the same row, or within a passed margin of being within the same row */
ET9BOOL ET9FARCALL SWCIPTable_PointsInSameRow(SWCIPTable *pThis, SBYTE2 *y1, SBYTE2 y2, SBYTE2 margin, ET9BOOL setY1)
{
    SWDbm*  pDbm = pThis->m_backend->m_pDbm;
    SBYTE2  rowNum1, rowNum2;
    ET9BOOL    nearTopBoundary1 = _FALSE, nearTopBoundary2 = _FALSE;
    ET9BOOL    nearBottomBoundary1 = _FALSE, nearBottomBoundary2 = _FALSE;

    for (rowNum1 = 0; rowNum1 < SWDbm_rowCnt(pDbm); rowNum1++)
    {
        if (*y1 <= pThis->rowBoundary[rowNum1])
        {
            nearBottomBoundary1 = (ET9BOOL)(*y1 >= (pThis->rowBoundary[rowNum1] - margin));
            if (rowNum1 >= 1)
                nearTopBoundary1 = (ET9BOOL)(*y1 <= (pThis->rowBoundary[rowNum1 - 1] + margin));
            break;
        }
    }
    for (rowNum2 = 0; rowNum2 < SWDbm_rowCnt(pDbm); rowNum2++)
    {
        if (y2 <= pThis->rowBoundary[rowNum2])
        {
            nearBottomBoundary2 = (ET9BOOL)(y2 >= (pThis->rowBoundary[rowNum2] - margin));
            if (rowNum2 >= 1)
                nearTopBoundary2 = (ET9BOOL)(y2 <= (pThis->rowBoundary[rowNum2 - 1] + margin));
            break;
        }
    }
    if ((rowNum1 == rowNum2) ||
        ((rowNum2 == (rowNum1 + 1)) && (nearBottomBoundary1 || nearTopBoundary2)) ||
        ((rowNum1 == (rowNum2 + 1)) && (nearTopBoundary1 || nearBottomBoundary2))
        )
    {
        if (setY1)
            *y1 = (*y1 + y2) / 2;
        return(_TRUE);
    }
    return(_FALSE);
}



/*============================================================================= */
/* Function:    SWCIPTable_CheckMultipleIPsSequence() */
/* Parameters:  SWCIPTableRow *ipData: IP to check and determine how many of the */
/*              following IPs could potentially be included as a group of IPs */
/*              used to create a double-letter "scribble" gesture.  The number */
/*              determined is set in the IP field ForwardMultipleCount of ipData, */
/*              and also in the IP field BackwardMultipleCount of the last IP */
/*              of the sequence (as long as BackwardMultipleCount is still set */
/*              to -1) */
/* Returns:     Number of IPs in sequence */
/*============================================================================= */

SBYTE2 ET9FARCALL SWCIPTable_CheckMultipleIPsSequence(SWCIPTable *pThis, SBYTE2 checkIPIndex)
{
    SWCIPTableRow *ipData, *ipNext, *ipLast, *ipPrev;
    SBYTE2      multipleIPCount, doubleIPIndex, averageX, averageY, currentX, currentY, firstY, lastX, lastY, lastFixed;
    BYTE2       pathDist, straightDist;
    _SWPoint     gesturePos;
    ET9BOOL        checkAnother, pointsOK, includeAll;

    multipleIPCount = 0;    /* Count number of IPs beyond current IP. */
    if (pThis->m_backend->m_pSearchDB->m_wIPTableCount <= 2)         /* Don't treat short straight path as double letter gesture */
        return(0);
    ipData = ipLast = pThis->m_backend->m_pSearchDB->m_aIPTable[checkIPIndex];
    doubleIPIndex = ipData->m_DoubleIPIndex;
    lastX = averageX = ipData->m_IPLocation.x;
    firstY = lastY = averageY = ipData->m_IPLocation.y;     /* Sequence of multiple IPs needs to be more or less in a horizontal row (heuristic) */
    checkAnother = _TRUE;
    lastFixed = ipLast->m_FixedIndexOut;
    /* If this is not part of a DoubleLetter gesture, then require that the path "doubles back" on itself */
    /* at each IP of the scribble gesture *including* the first IP (if the first gesture IP is not also the PenDown IP) */
    includeAll = _FALSE;
    if (checkIPIndex > 0)
    {
        ipPrev = pThis->m_backend->m_pSearchDB->m_aIPTable[checkIPIndex - 1];
    }
    else
    {
        ipPrev = NULL;
        /* Check whether entire path is about the size of a key.  If so, allow entire path to be one scribble */
        if ((pThis->m_backend->m_pSearchDB->pathWidth <= SINGLE_KEY_PATH_MAX_WIDTH) && (pThis->m_backend->m_pSearchDB->pathHeight <= SINGLE_KEY_PATH_MAX_HEIGHT))
        {
            if (pThis->m_backend->m_pSearchDB->pathCenterFromRow <= SINGLE_KEY_PATH_ROW_CENTER_MARGIN)
                includeAll = _TRUE;
        }
    }
    checkIPIndex++;
    pointsOK = _TRUE;
    while (checkAnother)
    {
        checkAnother = _FALSE;
        if (checkIPIndex < pThis->m_backend->m_pSearchDB->m_wIPTableCount)
        {
            ipNext = pThis->m_backend->m_pSearchDB->m_aIPTable[checkIPIndex];
            if ((ipNext->m_exitIndex < 0) && (ipNext->m_IPType != Exit))               /* Scribble gesture cannot continue through an Exit IP */
            {
                checkIPIndex++;

                currentX = ipNext->m_IPLocation.x;
                currentY = ipNext->m_IPLocation.y;

                if (multipleIPCount == 0)   /* Don't let center of scribble gesture "drift" with additional points */
                {
                    if ((doubleIPIndex > 0) && (ipNext->m_DoubleIPIndex == doubleIPIndex))
                    {
                        averageX = gesturePos.x = ipNext->gestureLoc.x;
                        averageY = gesturePos.y = ipNext->gestureLoc.y;
                    }
                    else
                    {
                        averageX = gesturePos.x = (SBYTE2)(lastX + currentX) / 2;
                        averageY = gesturePos.y = (SBYTE2)(lastY + currentY) / 2;
                        /*// Check now whether gesture also "doubles back" at first IP of potential scribble gesture */
                        /*if (ipPrev != NULL) */
                        /*{ */
                        /*    // Return failure if previous IP and first two are "co-linear" along X dimension */
                        /*    if (((ipPrev->m_IPLocation.x < lastX) && (lastX < currentX)) || ((ipPrev->m_IPLocation.x > lastX) && (lastX > currentX))) */
                        /*        return(0); */
                        /*} */
                    }
                }
                /* All points must be in same keyboard row... */
                pointsOK = (SWCIPTable_PointsInSameRow(pThis, &firstY, currentY, SCRIBBLE_GESTURE_SAME_ROW_MARGIN, _TRUE) && !includeAll);      /* Skip detailed checks if already including all IPs (or all IPs in current Double IP) */
                if (pointsOK)
                {
                    /* Check whether the current two successive IPs are connected by a relatively straight path */
                    pathDist = _SWCSearchDB_GetZ1PathLength(pThis->m_backend->m_pSearchDB, lastFixed, ipNext->m_FixedIndexIn);
                    straightDist = _SWPoint_distance(&ipLast->m_IPLocation, &ipNext->m_IPLocation);
                    /* Check path-to-distance ratio if successive points meet either minimum distance or minimum path length  */
                    /*   separation.  Otherwise, assume points pass the test.  */
                    if ((straightDist >= MULTIPLE_IP_PATH_DISTANCE_RATIO_CHECK_DISTANCE_THRESHOLD) ||
                        (pathDist >= MULTIPLE_IP_PATH_DISTANCE_RATIO_CHECK_PATH_THRESHOLD))
                    {
                        if (straightDist < MULTIPLE_IP_THRESHOLD_DISTANCE)  /* Allow for 50% over straight-line path if points are relatively close */
                        {
                            pointsOK = (ET9BOOL)(pathDist < (straightDist + (straightDist>>1)));
                        }
                        else    /* Allow for 6.25% over straight-line path when points are more widely separated */
                        {
                            pointsOK = (ET9BOOL)(pathDist < (straightDist + (straightDist>>4)));
                        }
#if DEBUG_IPT2
                        if (!pointsOK)
                        {
                            if (straightDist < MULTIPLE_IP_THRESHOLD_DISTANCE)
                            {
                                Log(SWLogger_DEBUG, L" MultIPs %d - %d fail relaxed curve threshold [%d > (1.5 * %d)]\n", (checkIPIndex - 1), checkIPIndex, pathDist, straightDist);
                            }
                            else
                                Log(SWLogger_DEBUG, L" MultIPs %d - %d fail curve threshold [%d > (1.0625 * %d)]\n", (checkIPIndex - 1), checkIPIndex, pathDist, straightDist);
                        }
#endif
                    }
                }
                if (pointsOK)
                {
                    pointsOK = (ET9BOOL)((absGeo(currentY - averageY) <= MULTIPLE_IP_VERTICAL_MARGIN) && (absGeo(currentX - averageX) <= MULTIPLE_IP_HORIZONTAL_MARGIN));
#if DEBUG_IPT2
                    if (!pointsOK)
                    {
                        if (ipNext->m_IPType == PenUp)
                        {
                            Log(SWLogger_DEBUG, L" MultIPs %d (PenUp!) fails MARGIN test: Av: [%d, %d] Current: [%d, %d] \n", checkIPIndex, averageX, averageY, currentX, currentY);
                        }
                        else
                        {
                            Log(SWLogger_DEBUG, L" MultIPs %d fails MARGIN test: Av: [%d, %d] Current: [%d, %d] \n", checkIPIndex, averageX, averageY, currentX, currentY);
                        }
                    }
#endif
                }
#if SCRIBBLE_IS_HORIZONTAL
                /* If points are within range (and far enough apart), perform a more rigorous test */
                if (pointsOK && (straightDist >= MULTIPLE_IP_THRESHOLD_DISTANCE))
                {
                    float xDif = (float)absGeo(currentX - lastX);
                    float yDif = (float)absGeo(currentY - lastY);
                    pointsOK = ((yDif / xDif) < Y_TO_X_SUM_MAX_RATIO);
#if DEBUG_IPT2
                    if (!pointsOK)
                    {
                        if (ipNext->m_IPType == PenUp)
                            Log(SWLogger_DEBUG, L" MultIPs %d (PenUp!) fails Y-to_X RATIO test: Av: [%d, %d] Current: [%d, %d] \n", checkIPIndex, averageX, averageY, currentX, currentY);
                        else
                            Log(SWLogger_DEBUG, L" MultIPs %d fails Y-to_X RATIO test: Av: [%d, %d] Current: [%d, %d] \n", checkIPIndex, averageX, averageY, currentX, currentY);
                    }
#endif
                }
#endif
                /*if (!pointsOK && (doubleIPIndex > 0) && !includeAll) */
                if (!pointsOK && !includeAll)
                {
                    if ((doubleIPIndex > 0) && (ipNext->m_DoubleIPIndex == doubleIPIndex))
                    {
                        pointsOK = _TRUE;
                    }
                    else if ((ipNext->m_DoubleIPIndex == (doubleIPIndex + 1)) &&
                             SWCIPTable_PointsInSameRow(pThis, &gesturePos.y, ipNext->gestureLoc.y, SCRIBBLE_GESTURE_SAME_ROW_MARGIN, _FALSE) &&
                             (_SWPoint_distance(&gesturePos, &ipNext->gestureLoc) < MERGE_DOUBLE_IP_DISTANCE))
                    {
                        doubleIPIndex++;        /* Keep going with adjacent Double IP, but don't change original gesture location (no "drifting") */
                        pointsOK = _TRUE;
                    }
                }
                if (pointsOK || includeAll)
                {
                    if (ipNext->m_IPType == PathDivision)
                    {
                        ipNext->m_IPType = Multiple;        /* treat as Multiple if anomalously created within scribble gesture */
                        ipNext->m_RowFilled = CALCULATE_ROW;
                    }
                    lastX = currentX;
                    lastY = currentY;
                    multipleIPCount++;
                    ipData->m_ForwardMultipleCount = multipleIPCount;   /* Keep updating forward count */
                    if (ipNext->m_BackwardMultipleCount == 0)           /* Set backward count if not already set previously */
                        ipNext->m_BackwardMultipleCount = multipleIPCount;
                    AlwaysAssert(ipNext->m_BackwardMultipleCount >= multipleIPCount);       /* If previously set, should be to larger value */
                    ipLast = ipNext;
                    lastFixed = ipLast->m_FixedIndexOut;
                    checkAnother = _TRUE;
                }
            }
        }
    }
    /* If this is a multiply-repeated double letter scribble gesture on a key (which may have a single-letter word), then */
    /*   set the m_ForceShowWCW flag to force the display of the word choice list (i.e. to enable selection of "aaa" from the "a" key) */
    if ((multipleIPCount >= (pThis->m_backend->m_pSearchDB->m_wIPTableCount - 1)) && (multipleIPCount >= 5) && (ipData->m_IPType == PenDown))
        pThis->m_backend->m_pSearchDB->m_ForceShowWCW = _TRUE;
    return(multipleIPCount);
}   /* SWCIPTable_CheckMultipleIPsSequence() */

/*============================================================================= */
/* Function:    SWCIPTable_reProcessIPTable() */
/* Description: Re-Process the SearchDB() IP Table with respect to updated (searchLevel == ThisSearchVal) thresholds. */
/*============================================================================= */

void ET9FARCALL SWCIPTable_reProcessIPTable(SWCIPTable *pThis, ALGORITHM_TYPE searchAlgorithm)
{
    SWCIPTableRow *ipData0, *ipData1, *ipData2;
    SWCIPTableRow **pIPTable;
    BYTE1       key;
    SBYTE2      IPIndex, i, table;
    BYTE4       threshold;
    BYTE2       Distance, SegDistance, wDistance, wPenDownDistance;
    _SWPoint    KeyLoc;
    ET9BOOL     saveForward, reCalc;
    IPType      ipData1Type;

    SWDbm*  pDbm = pThis->m_backend->m_pDbm;
    if (pThis->m_backend->m_pSearchDB->algRecalcsThresholds[searchAlgorithm])
    {
        for (i = 0; i < MaxIPType; i++)
        {
            threshold = ((SWCIPAnalyzer_GetSpeedFactorDW(pThis->m_backend->pIPAnalyzer) * (pThis->IPthreshold8[i][SearchLevelMaxVal] - pThis->IPthreshold8[i][SearchLevelMinVal])) / MAX_SPEED_FACTOR);
            pThis->IPthreshold8[i][ThisSearchVal] = (BYTE2)(pThis->IPthreshold8[i][SearchLevelMinVal] + threshold);
        }
        threshold = pThis->SegmentThreshold8[SearchLevelMinVal] + ((SWCIPAnalyzer_GetSpeedFactorDW(pThis->m_backend->pIPAnalyzer) * (pThis->SegmentThreshold8[SearchLevelMaxVal] - pThis->SegmentThreshold8[SearchLevelMinVal])) / MAX_SPEED_FACTOR);
        pThis->SegmentThreshold8[ThisSearchVal] = (BYTE2)threshold;

        threshold = ((SWCIPAnalyzer_GetSpeedFactorDW(pThis->m_backend->pIPAnalyzer) * (pThis->PenUpOvershootIPThreshold8[SearchLevelMaxVal] - pThis->PenUpOvershootIPThreshold8[SearchLevelMinVal])) / MAX_SPEED_FACTOR);
        pThis->PenUpOvershootIPThreshold8[ThisSearchVal] = (BYTE2)(pThis->PenUpOvershootIPThreshold8[SearchLevelMinVal] + threshold);

        threshold = ((SWCIPAnalyzer_GetSpeedFactorDW(pThis->m_backend->pIPAnalyzer) * (pThis->PenUpOvershootSegmentThreshold8[SearchLevelMaxVal] - pThis->PenUpOvershootSegmentThreshold8[SearchLevelMinVal])) / MAX_SPEED_FACTOR);
        pThis->PenUpOvershootSegmentThreshold8[ThisSearchVal] = (BYTE2)(pThis->PenUpOvershootSegmentThreshold8[SearchLevelMinVal] + threshold);

        threshold = ((SWCIPAnalyzer_GetSpeedFactorDW(pThis->m_backend->pIPAnalyzer) * (pThis->PenUpUndershootIPThreshold8[SearchLevelMaxVal] - pThis->PenUpUndershootIPThreshold8[SearchLevelMinVal])) / MAX_SPEED_FACTOR);
        pThis->PenUpUndershootIPThreshold8[ThisSearchVal] = (BYTE2)(pThis->PenUpUndershootIPThreshold8[SearchLevelMinVal] + threshold);

        threshold = ((SWCIPAnalyzer_GetSpeedFactorDW(pThis->m_backend->pIPAnalyzer) * (pThis->PenUpUndershootSegmentThreshold8[SearchLevelMaxVal] - pThis->PenUpUndershootSegmentThreshold8[SearchLevelMinVal])) / MAX_SPEED_FACTOR);
        pThis->PenUpUndershootSegmentThreshold8[ThisSearchVal] = (BYTE2)(pThis->PenUpUndershootSegmentThreshold8[SearchLevelMinVal] + threshold);

        threshold = ((SWCIPAnalyzer_GetSpeedFactorDW(pThis->m_backend->pIPAnalyzer) * (pThis->PenDownOvershootIPThreshold8[SearchLevelMaxVal] - pThis->PenDownOvershootIPThreshold8[SearchLevelMinVal])) / MAX_SPEED_FACTOR);
        pThis->PenDownOvershootIPThreshold8[ThisSearchVal] = (BYTE2)(pThis->PenDownOvershootIPThreshold8[SearchLevelMinVal] + threshold);

        threshold = ((SWCIPAnalyzer_GetSpeedFactorDW(pThis->m_backend->pIPAnalyzer) * (pThis->PenDownOvershootSegmentThreshold8[SearchLevelMaxVal] - pThis->PenDownOvershootSegmentThreshold8[SearchLevelMinVal])) / MAX_SPEED_FACTOR);
        pThis->PenDownOvershootSegmentThreshold8[ThisSearchVal] = (BYTE2)(pThis->PenDownOvershootSegmentThreshold8[SearchLevelMinVal] + threshold);

        threshold = pThis->PointIPSumLimit[0] + ((SWCIPAnalyzer_GetSpeedFactorDW(pThis->m_backend->pIPAnalyzer) * (pThis->PointIPSumLimit[SearchLevelMaxVal] - pThis->PointIPSumLimit[SearchLevelMinVal])) / MAX_SPEED_FACTOR);
        pThis->PointIPSumLimit[ThisSearchVal] = (BYTE2)threshold;
    }
    if (pThis->m_backend->m_pSearchDB->algNoReprocIPTable[searchAlgorithm])
    {
        DebugAssert(_FALSE);
        return;
    }
#if DEBUG_IPT1C3A
    SWCIPTable_dumpIPs(pThis, &pThis->m_backend->m_pSearchDB->m_aIPTable, &pThis->m_backend->m_pSearchDB->m_aIPTable2, 98, NULL);
#endif
    /*if (!pThis->m_backend->m_pSearchDB->algNoReprocIPTable[searchAlgorithm]) */
    saveForward = pThis->m_backend->m_pSearchDB->forward;
    pThis->m_backend->m_pSearchDB->forward = _TRUE;     /* Set now for following nested calls to GetZ1PathLengthXXX() */
    /* SavedCodeSegments/SearchDB.cpp/111 */
    /* First set multiple IP counts.  Then check for anomalies below */
    if (!pThis->m_backend->m_pSearchDB->calledCheckMultipleIPsSequence)
    {
        for (IPIndex = 0; IPIndex < pThis->m_backend->m_pSearchDB->m_wIPTableCount; IPIndex++)
            SWCIPTable_CheckMultipleIPsSequence(pThis, IPIndex);
        pThis->m_backend->m_pSearchDB->calledCheckMultipleIPsSequence = _TRUE;
    }
        /* Check Segment Distances in Exit IPs */
        /* TODO: With sufficiently "round" input paths and multiple Exits, a key may have multiple valid segment matches between IPs. */
        /* In SetKeyDistances(), when the current IP references a valid preceding Exit IP (which may itself reference yet another preceding */
        /* Exit IP - 3 levels, however, should be a reasonable limit here), two or more segment matching locations should be defined.  The */
        /* later match would still receive preference in matching UNLESS this caused a problem with matching other keys in sequence.  In this */
        /* approach, segment data from the associated Exit IP would NOT be copied into the referencing IP (since this would over-write the */
        /* needed data) */
        /* SavedCodeSegments/SearchDB.cpp/112 */
    {
#if DEBUG_SHOW_RECALC_COUNTS
        if (searchAlgorithm >= ALG_MGD_PREV)
            Log(SWLogger_DEBUG, TEXT(" \nOriginally calculated: %d IP Distances /  %d Segment Distances\n"), pThis->IPDistanceCalcCount, pThis->SegDistanceCalcCount);
        pThis->IPDistanceCalcCount = pThis->SegDistanceCalcCount = 0;
#endif
        for (table = 0; table < 2; table++)
        {
            ET9BOOL mainTable;
            SBYTE2  tableSize;
            if (table == 0)
            {
                pIPTable = pThis->m_backend->m_pSearchDB->m_aIPTable;
                tableSize = pThis->m_backend->m_pSearchDB->m_wIPTableCount;
                mainTable = _TRUE;
            }
            else
            {
                pIPTable = pThis->m_backend->m_pSearchDB->m_aIPTable2;
                tableSize = pThis->m_backend->m_pSearchDB->m_wIPTable2Count;
                mainTable = _FALSE;
            }
            for (IPIndex = 0; IPIndex < tableSize; IPIndex++)
            {
                ipData1 = pIPTable[IPIndex];
                if (mainTable && (IPIndex < (tableSize - 1)))
                    ipData2 = pIPTable[IPIndex + 1];
                else
                    ipData2 = NULL;
                reCalc = SWCIPTable_IPCalcNeeded(pThis, ipData1, ADJ_IP_DISTANCES1);
                ipData1Type = ipData1->m_IPType;
                if (ipData1Type == Entry)
                {
                    Log(SWLogger_INFO, SWTEXT("Swype Event: %d \r\n"), 411)
                    continue;
                }
                if (mainTable && (IPIndex >= 1))
                {
                    ipData0 = pIPTable[IPIndex - 1];
                    if (ipData0->m_IPType == Exit)
                    {
                        ipData0 = _SWCSearchDB_GetExitEntryIP(pThis->m_backend->m_pSearchDB, ipData0->m_IPIndex, Entry);
                    /*  if (ipData0 == NULL)
                            Z1ErrorOutput(412); */
                    }
                    if (SWCIPTable_IPCalcNeeded(pThis, ipData0, IP_DISTANCES1))       /* IP may need basic IP distances for following segment calculations */
                        SWCIPTable_SetIPDistances1(pThis, ipData0);
                }
                else
                    ipData0 = NULL;
                for (key = 0; key < SWDbm_letterKeyCnt(pDbm); key++)
                {
                    ET9BOOL alreadyDone = _FALSE;
                    SWDbm_getKeyCenterScaled(pDbm, key, &KeyLoc);     /*lint !e613        // Get location of key */
                    /* 0 value means it has not yet been set */
                    if (!ipData1->m_IPDistance8[key] || reCalc) /* 0 value means it has not yet been set */
                    {
                        SWCIPTable_SetIPDistance8(pThis, ipData1, ipData0, ipData2, key, ThisSearchVal);
                        alreadyDone = _TRUE;     /* No need to re-calculate below */
                    }
                    if (ipData0 != NULL)
                    {   /* 0 value means it has not yet been set */
                        if (!ipData1->m_SegmentDistance8[key] || reCalc)
                            SWCIPTable_SetSegmentDistance8(pThis, ipData0, ipData1, key, ThisSearchVal);
                        else if (ipData1->m_SegmentDistance8[key] > pThis->SegmentThreshold8[ThisSearchVal])
                            ipData1->m_SegmentWDistance8[key] = W_DISTANCE8_MAX;
                        else if (ipData1->m_SegmentWDistance8[key] == W_DISTANCE8_MAX)  /* Updated threshold is lower than original - set appropriate weighted distance */
                            SWCIPTable_SetSegmentDistance8(pThis, ipData0, ipData1, key, ThisSearchVal);
                    }
                    if ((ipData1Type != Exit) && !alreadyDone)
                    {
                        if ((ipData1->m_IPFlags & OVERSHOOT_IP) == 0)
                        {
                            if (ipData1->m_IPDistance8[key] > pThis->IPthreshold8[ipData1Type][ThisSearchVal])
                                ipData1->m_IPWDistance8[key] = W_DISTANCE8_MAX;
                            /* If updated threshold is larger than original - set appropriate weighted distance now */
                            else if (ipData1->m_IPWDistance8[key] == W_DISTANCE8_MAX)
                                SWCIPTable_SetIPDistance8(pThis, ipData1, ipData0, ipData2, key, ThisSearchVal);  /* Use more complex code in SetIPDistance8() for "bottom-row" cases, etc. */
                        }
                        else    /* Use more complex code in SetIPDistance8() for special overshoot cases */
                            SWCIPTable_SetIPDistance8(pThis, ipData1, ipData0, ipData2, key, ThisSearchVal);
                    }
                }
            }
        }
        /* SavedCodeSegments/SearchDB.cpp/117 - Adjust penDown distances for possible "overshoot" */
        /* Adjust penDown distances for possible "overshoot" - i.e. starting the trace too early but passing relatively closely through the intended key */
        if (pThis->m_backend->m_pSearchDB->m_wIPTableCount > 1)      /* Sanity check */
        {
            ipData0 = pThis->m_backend->m_pSearchDB->m_aIPTable[0];
            ipData1 = pThis->m_backend->m_pSearchDB->m_aIPTable[1];
            for (key = 0; key < SWDbm_letterKeyCnt(pDbm); key++)
            {
                Distance = ipData0->m_IPDistance8[key];
                SegDistance = ipData1->m_SegmentDistance8[key];
                SWDbm_getKeyCenterScaled(pDbm, key, &KeyLoc);     /*lint !e613        // Get location of key */
    #if USE_DISTANCE2Y8_IP
                wDistance = _SWPoint_distance2y8(&pThis->m_backend->sScreenGeometry, &KeyLoc, &ipData0->m_IPLocation);
    #else
                wDistance = Distance;
    #endif
                /* Don't adjust distance unless overshoot exceeds a minimum threshold, and path segment is close enough to key (and closer than IP itself) */
                if ((Distance > MIN_IP_DISTANCE8_PENDOWN_OVERSHOOT) && (SegDistance < pThis->PenDownOvershootSegmentThreshold8[ThisSearchVal]) && (SegDistance < Distance) && SegDistance)
                {
                    /* Test against less stringent "PenDown Overshoot" Segment threshold */
                    if (Distance <= pThis->PenDownOvershootIPThreshold8[ThisSearchVal])
                    {   /* Set weighted and unweighted distances to weighted average of the two values */
                        /*wPenDownDistance = sw_max(((SegDistance + wDistance) / 2), MIN_IP_DISTANCE8_PENDOWN_OVERSHOOT); */
                        /*wPenDownDistance = sw_max(((SegDistance + (2 * wDistance)) / 3), MIN_IP_DISTANCE8_PENDOWN_OVERSHOOT); */
                        wPenDownDistance = (BYTE2)sw_max(((SegDistance + (2 * wDistance)) / 3), MIN_IP_DISTANCE8_PENDOWN_OVERSHOOT);
                        /* Use overshoot-adjusted PenUp weighted distance only if less than standard measurement */
                        if (wPenDownDistance < wDistance)
                            wDistance = wPenDownDistance;
                        if (SegDistance < Distance)
                        {
                            Distance = ipData0->m_IPAdjDistance8[key] = (SegDistance + (2 * Distance)) / 3;
                        }
                    }
                    if (Distance > pThis->IPthreshold8[PenDown][ThisSearchVal])
                        ipData0->m_IPWDistance8[key] = W_DISTANCE8_MAX;
                    else
                    {
                        /* If less than minimum distance, then set as average of actual distance and minimum value to */
                        /* reduce the influence of paths that happen to coincide closely with keys of low-freq words */
                        if (wDistance < MIN_PENDOWN_WDISTANCE8)
                            wDistance = (wDistance + MIN_PENDOWN_WDISTANCE8) / 2;
                        ipData0->m_IPWDistance8[key] = (BYTE2)(long)(wDistance * ipData0->m_fIPWeight);
                    }
                }
            }
        }
    }
#if DEBUG_SHOW_RECALC_COUNTS
    if (searchAlgorithm >= ALG_MGD_PREV)
        Log(SWLogger_DEBUG, TEXT(" \nRe-calculated: %d IP Distances /  %d Segment Distances\n"), pThis->IPDistanceCalcCount, pThis->SegDistanceCalcCount);
    pThis->IPDistanceCalcCount = pThis->SegDistanceCalcCount = 0;
#endif
#if DEBUG_IPT1C3B
    SWCIPTable_dumpIPs(pThis, &pThis->m_backend->m_pSearchDB->m_aIPTable, &pThis->m_backend->m_pSearchDB->m_aIPTable2, 99, NULL);
#endif

    pThis->m_backend->m_pSearchDB->forward = saveForward;      /* Restore to previous value */
    /* SavedCodeSegments/SearchDB.cpp/156 */
}   /* SWCIPTable_reProcessIPTable() */


#if ENABLE_IP_RENDERING
/*============================================================================= */
/* Function:    SWCIPTable_RenderData() */
/* Parameters: */
/* Description: Draw the smoothed path, then traverse the inflection point path */
/*              and indicate all inflection points. */
/*============================================================================= */

ET9BOOL ET9FARCALL SWCIPTable_RenderData(SWCIPTable *pThis, ET9BOOL showPath)
{
    SBYTE2  keyIndex;
    _SWPoint KeyLoc;
    ET9BOOL    useSearchData;

    SWOS* pOS = SWOS::GetInstance();
    SWDbm* pDbm = SWDbm_GetInstance();
    DebugAssert(pOS != NULL && pDbm != NULL);

    SWCIPTable_GetIPTableArray(pThis);   /* Temporarily lock the IP Table array while rendering */
    /* only render if data was analyzed and not cancelled */
    if (!Analyzed)
    {
        return _FALSE;
    }
    /* SavedCodeSegments/SearchDB.cpp/109 */
    DisplayingTable = _TRUE;     /* Flag that RenderData() is running... */

    SBYTE2 tableCount = SWCIPTable_GetIPTableSize(pThis);
    SBYTE2 fixedIndex, tableIndex;
    BYTE2  searchLevelParam;
    SWCIPTableRow *ipData, *ipNext;
    _SWPoint  pt, pt2, fixedLoc, fixedLoc2;
    ET9BOOL   bPenUp, bEntry, bExit, bSpecial;

    useSearchData = _TRUE;
    if (tableCount > 0)
    {
        SBYTE2 PenDownIndex = 0;
        ipData = SWCIPTable_GetIPTableRow(pThis, PenDownIndex);       /* Get PenDown IP */
        if ((ipData->m_IPType != Exit) && (ipData->m_IPType != Entry))
            useSearchData = _FALSE;
    }
    if (useSearchData)
    {
        tableCount = (SBYTE2)pThis->m_backend->m_pSearchDB->m_aIPTable.size();
        searchLevelParam = 1;       /* Triggers call to GetZ1FixedPoint() in GetFixedPoint() */
    }
    else
        searchLevelParam = 0;               /* Triggers call to m_backend.pIPAnalyzer->GetFixedPoint() in GetFixedPoint() */
    for (tableIndex = 0; tableIndex < tableCount; tableIndex++)
    {
        if (useSearchData)
            ipData = pThis->m_backend->m_pSearchDB->m_aIPTable[tableIndex];        /* Get next IP */
        else
            ipData = SWCIPTable_GetIPTableRow(pThis, tableIndex);     /* Get next IP */
        bPenUp = (ipData->m_IPType == PenUp);
        bEntry = (ipData->m_IPType == Entry);
        bExit = (ipData->m_IPType == Exit);
        bSpecial = _FALSE;
        if (!bPenUp)
            bPenUp = (tableIndex == (tableCount - 1));
        /* Draw next segment of path before drawing IP itself */
        if (showPath && !bPenUp /* && !bExit */)
        {
            fixedLoc = SWCIPTable_GetFixedPoint(pThis, ipData->m_FixedIndexOut, searchLevelParam, 20);
            pOS->DrawLine(SWOS::InputWindow, ipData->m_IPLocation, fixedLoc, SWOS::COLOR_RED);
        }
        /* Choose color based on IP Type */
        SWOS::COLOR pColor = SWOS::COLOR_RED;
        switch (ipData->m_IPType)
        {
        case PenDown:                       /* Pen Down is Black */
        case PenUp:                         /* PenUp is Black */
        default:
            pColor = SWOS::COLOR_BLACK;
            break;
        case SoftDown:                      /* SoftDown is Green */
        case SoftUp:                        /* SoftUp is Green */
        case Entry:
        case Exit:
            pColor = SWOS::COLOR_GREEN;
            break;
        case Pause:                         /* Pause is Magenta */
        case PauseAngle:                    /* PauseAngle is Magenta */
            pColor = SWOS::COLOR_MAGENTA;
            break;
        case DoubleLetter:                  /* DoubleLetter is Red */
            pColor = SWOS::COLOR_RED;
            break;
        case PathDivision:                  /* PathDivision is Yellow */
            pColor = SWOS::COLOR_YELLOW;
            break;
        case Angle:                         /* Angle is Blue */
        case Tap:                           /* Tap is Blue */
            pColor = SWOS::COLOR_BLUE;
            break;
        }
        if (ipData->m_MergeCount > 1)
            pColor = SWOS::COLOR_YELLOW;            /* Any merged IP is yellow */
        if (ipData->m_DoubleIPIndex > 0)
        {
            pColor = SWOS::COLOR_RED;           /* Any double gesture IP is Red */
            if ((ipData->m_IPType == PenDown) || (ipData->m_IPType == PenUp))
                pColor = SWOS::COLOR_CYAN;          /* PenDown/PenUp IP are Cyan */
        }

        /* Draw the IP */
        {
            SBYTE2 squareSize = (showPath && (pThis->RenderIPsFlag != 1)) ? 6 : 8;
            _SWPoint xyz(ipData->m_IPLocation.x, ipData->m_IPLocation.y);
            if (bSpecial)       /* Draw distinct black and red outline around special IP */
            {
                pOS->DrawSquare(SWOS::InputWindow, xyz, (squareSize + 2), _FALSE, SWOS::COLOR_BLACK);    /* Draw a square. */
                pOS->DrawSquare(SWOS::InputWindow, xyz, squareSize, _FALSE, SWOS::COLOR_RED);            /* Draw a square. */
                squareSize -= 2;
            }
            pOS->DrawSquare(SWOS::InputWindow, xyz, squareSize, _TRUE, pColor);  /* Draw a square. */
            if (ipData->m_DoubleIPIndex > 0)        /* Show FixedIndexIn and Out locations */
            {
                fixedLoc = SWCIPTable_GetFixedPoint(pThis, (ipData->gestureIn + 1), searchLevelParam, 21);
                pColor = SWOS::COLOR_MAGENTA;                                   /* FixedIndexIn is Magenta */
                pOS->DrawSquare(SWOS::InputWindow, fixedLoc, 4, _TRUE, pColor);  /* Draw a square. */

                fixedLoc = SWCIPTable_GetFixedPoint(pThis, ipData->gestureOut, searchLevelParam, 22);
                pColor = SWOS::COLOR_CYAN;                                      /* FixedIndexOut is Cyan */
                pOS->DrawSquare(SWOS::InputWindow, fixedLoc, 4, _TRUE, pColor);  /* Draw a square. */
            }
        }
        if (showPath && !bPenUp /* && !bExit */ && (tableIndex < (tableCount - 1))) /* Continue path to next IP, drawing each fixed point */
        {
            if (useSearchData)
                ipNext = pThis->m_backend->m_pSearchDB->m_aIPTable[tableIndex + 1];        /* Get next IP */
            else
            {
                SBYTE2 nextIndex = tableIndex + 1;
                ipNext = SWCIPTable_GetIPTableRow(pThis, nextIndex);      /* Get following IP */
            }
            for (fixedIndex = ipData->m_FixedIndexOut + 2; fixedIndex <= ipNext->m_FixedIndexIn; fixedIndex ++)
            {
                /* draw a line from previous point to this point */
                fixedLoc2 = SWCIPTable_GetFixedPoint(pThis, fixedIndex, searchLevelParam, 23);
                pOS->DrawLine(SWOS::InputWindow, fixedLoc, fixedLoc2, SWOS::COLOR_TRACE);
                fixedLoc = fixedLoc2;
            }
        }
        /* Draw a Red rectangle at the center of each key within threshold of PenDown and PenUp IPs */

        if (showPath && (pThis->RenderIPsFlag == 2) && ((tableIndex == 0) || (bPenUp && !bExit)))
        {
            SBYTE2  yOffset;
            if (tableIndex == 0)
            {
                pColor = SWOS::COLOR_RED;
                yOffset = 2;
            }
            else
            {
                pColor = SWOS::COLOR_CYAN;
                yOffset = -2;
            }
            for (keyIndex = 0; keyIndex < SWDbm_letterKeyCnt(pDbm); keyIndex++)
            {
                if (ipData->m_IPWDistance8[keyIndex] != W_DISTANCE8_MAX)
                {
                    SWDbm_getKeyCenterScaled(pDbm, (BYTE1)keyIndex, &KeyLoc);     /* Get location of key */
                    KeyLoc.y += yOffset;
                    pOS->DrawSquare(SWOS::InputWindow, KeyLoc, 4, _TRUE, pColor);    /* Draw a square. */
                }
            }
        }
        /* Skip PenDown since no preceding segment */
        if (showPath && (pThis->RenderIPsFlag == 2) && (tableIndex > 0) && !bExit)        /* Draw a Blue "plus" above the center of each key within threshold of path segment */
        {
            for (keyIndex = 0; keyIndex < SWDbm_letterKeyCnt(pDbm); keyIndex++)
            {
                if (ipData->m_SegmentWDistance8[keyIndex] != DISTANCE8_MAX)
                {
                    SWDbm_getKeyCenterScaled(pDbm, (BYTE1)keyIndex, &KeyLoc);     /* Get location of key */
                    KeyLoc.y += 8;      /* move mark to bottom of key */
                    pOS->DrawPlus(SWOS::InputWindow, KeyLoc, 3, SWOS::COLOR_BLUE);
                }
            }
        }
    }
/* SavedCodeSegments/SearchDB.cpp/25 */
    DisplayingTable = _FALSE;        /* Flag that RenderData() is done... */

    return _TRUE;
} /* SWCIPTable_RenderData() */

#endif

/* eof */
