/***************************************************************************************
 *
 *  WRITEPAD(r): Handwriting Recognition Engine (HWRE) and components.
 *  Copyright (c) 2001-2016 PhatWare (r) Corp. All rights reserved.
 *
 *  Licensing and other inquires: <developer@phatware.com>
 *  Developer: Stan Miasnikov, et al. (c) PhatWare Corp. <http://www.phatware.com>
 *
 *  WRITEPAD HWRE is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  THE MATERIAL EMBODIED ON THIS SOFTWARE IS PROVIDED TO YOU "AS-IS"
 *  AND WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR OTHERWISE,
 *  INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY OR
 *  FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL PHATWARE CORP.
 *  BE LIABLE TO YOU OR ANYONE ELSE FOR ANY DIRECT, SPECIAL, INCIDENTAL,
 *  INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER,
 *  INCLUDING WITHOUT LIMITATION, LOSS OF PROFIT, LOSS OF USE, SAVINGS
 *  OR REVENUE, OR THE CLAIMS OF THIRD PARTIES, WHETHER OR NOT PHATWARE CORP.
 *  HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH LOSS, HOWEVER CAUSED AND ON
 *  ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE
 *  POSSESSION, USE OR PERFORMANCE OF THIS SOFTWARE.
 *  See the GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with WritePad.  If not, see <http://www.gnu.org/licenses/>.
 *
 **************************************************************************************/

#ifndef LSTRIP

#include  "hwr_sys.h"
#include  "ams_mg.h"
#include  "lowlevel.h"

#include  "calcmacr.h"

#if  USE_FRM_WORD   /* up to the end (almost) */

#define  FRLINE_ONLY  _FALSE

/***********************************************/

#define  MIN_POINTS_IN_FRLINE      5
#define  MIN_POINTS_TO_WORDBREAK   6
#define  MIN_INTERVALS_TO_RELY_ON  3
#define  MAX_INTERVALS_TO_STORE    10  /* Must be > 2*prev. #def! */

#define  MAX_RECTANGLE             6   /*  Max ratio (dx/dy or dy/dx) */
                                       /* for considering "delta_in-  */
                                       /* terval" as meaningful.      */
                                       /* (at the 1st pass).          */
/***********************************************/

typedef  struct  {
    _SHORT   xRange;             /* ~one-half of the mean letter width   */
    _SHORT   yRange;             /* ~one-half of the mean letter height  */
    _LONG    ldxSum;             /*    Accumulated values                */
    _LONG    ldySum;             /*          for calculating             */
    _LONG    lNumIntervals;      /*                   x(y)Range.         */
    _SHORT   dxNoFrline;         /* Mean non-frline stroke width.        */
    _SHORT   dyNoFrline;         /* Mean non-frline stroke height.       */
    _BOOL    bIncrementalBreak;  /*  Is this fly-wordbreak or not?       */
} WORDBRKDATA;
typedef  WORDBRKDATA _PTR  p_WORDBRKDATA;

typedef  struct  {
    _SHORT  iBeg;           /*  1st point of the stroke        */
    _SHORT  iEnd;           /*  Last point of the stroke       */
    _RECT   box;            /*  Stroke bounding box.           */
    _SHORT  iStrk;          /*  Ordinal number of this stroke  */
    /* in strokes array.               */
    _SHORT  xMean;          /*  x-coord of "weight-center".    */
    _SHORT  yMean;          /*  y-coord of "weight-center".    */
    _BOOL   bMayBeFrline;   /*  If the stroke looks like the   */
    /* fraction (division) line.       */
    _BOOL   bIsWordEnd;     /*  If this stroke is the last one */
    /* for some found word.            */
} STRK;
typedef  STRK _PTR  p_STRK;

#define  DX_STRK(strk)  DX_RECT((strk)->box)
#define  DY_STRK(strk)  DY_RECT((strk)->box)

/***********************************************/

static  WORDBRKDATA  wbd = {0, 0, 0L, 0L, 0L, 0, _FALSE};

/***********************************************/
/*  General functions:  */

_BOOL   FindXYScale ( p_SHORT xArray, p_SHORT yArray,
                      _INT iFirst, _INT iLast,
                      p_WORDBRKDATA pwbd );
_BOOL   AllocLowDataBuffers ( low_type _PTR pLowData );
_VOID   DeallocLowDataBuffers ( low_type _PTR pLowData );
_BOOL   GetFilteredData ( _SHORT qhorda_filt,
                          low_type _PTR pLowData,
                          p_SHORT _PTR pxArray,
                          p_SHORT _PTR pyArray,
                          p_INT pnPoints);
_SHORT  FindAllStrokes ( p_SHORT xArray, p_SHORT yArray, _INT nPoints,
                         p_STRK strk, _INT nMaxStrokes,
                         p_WORDBRKDATA pwbd );
_BOOL   ExtractWords ( p_SHORT xArray, p_SHORT yArray,
                       p_STRK strk, _INT nBreaks,
                       p_WORDBRKDATA pwbd, p_BOOL pbWasBroken,
                       p_SHORT pn1stWrdPoints, p_SHORT pnFrLinePoints );
_VOID   UnfilterAnswer ( low_type _PTR pLowData,
                         p_SHORT pn1stWordPoints,
                         p_SHORT pnFrLinePoints,
                         _TRACE  trace );
/***********************************************/
/*  "Bricks" for building various analyses: */

_BOOL  IsFrline ( p_SHORT xArray, p_SHORT yArray,
                  p_STRK strkCur,
                  _BOOL bFrstStrk, _BOOL bLastStrk,
                  p_WORDBRKDATA pwbd );
_BOOL  StrokesFarEnough ( p_SHORT xArray, p_SHORT yArray,
                          p_STRK pStrk1, p_STRK pStrk2,
                          p_WORDBRKDATA pwbd );
_BOOL  CurUnderWord ( p_SHORT xArray, p_SHORT yArray,
                      p_STRK pStrk, p_WORDBRKDATA pwbd );
_BOOL  HorizLine_AmongNmrAndDnmr ( p_SHORT xArray, p_SHORT yArray,
                                   p_STRK strk, p_WORDBRKDATA pwbd );
_BOOL  Minus_NearWord ( p_STRK strkFrl, p_STRK strkNear,
                        p_WORDBRKDATA pwbd );
/*
_BOOL  chk_horiz_move ( p_SHORT xArray,  p_SHORT yArray,
                        _INT iLeft,  _INT iRight );
*/
/***********************************************/
/*  Misc. utilities:  */

_BOOL  UpperStroke ( p_SHORT yArray,
                     _INT left1, _INT right1,
                     _INT left2, _INT right2,
                     _SHORT yTolerance );
_BOOL  AboveStroke ( p_SHORT xArray, p_SHORT yArray,
                     _INT left1, _INT right1,
                     _INT left2, _INT right2,
                     _SHORT xTolerance,
                     _SHORT yTolerance );
_VOID  ScaleTrj ( p_SHORT x, p_SHORT y, _INT nPoints);

/***********************************************/


_VOID   ResetFrmWordBreak(_VOID)    /*  Should be called before the passing */
{                                   /* the first stroke of the new formula. */
    HWRMemSet ( &wbd, 0, sizeof(wbd) );
}
/***********************************************/

/*  The function breaks to words the "trace" with total of  */
/* "nPoints";  the search must be done only in last         */
/* "nLastStrokes" strokes.                                  */
/*  If bIncrementalBreak==_TRUE, then the already collected */
/* info from previous calls (if any) is used in addition to */
/* the new info.                                            */
/*  If bIncrementalBreak==_FALSE, then all the old info is  */
/* cleared and collection begins from scratch.              */
/*                                                          */
/*  The parameter "pn1stWrdPoints" makes sence and is treated*/
/* only if bIncrementalBreak==_TRUE.                        */
/*                                                          */
/*  Where the wordsplit must be, the function puts 2        */
/* adjacent BREAKS, so that Gavrik will mark this place     */
/* as the new word begin (if bIncrementalBreak==_FALSE).    */

_SHORT  FrmWordBreak ( _TRACE trace, _INT  nPoints,
                      _BOOL bIncrementalBreak,
                      p_SHORT pn1stWrdPoints,
                      p_SHORT pnLastWrdPoints )
{
    _INT       iFirstPoint, iLastPoint;
    _INT       nFrLinePoints;
    _BOOL      bWasBroken = _FALSE;
    
    STRK       strk[MAX_STR];
    _INT       nStrokes;
    p_SHORT    xArray = _NULL;
    p_SHORT    yArray = _NULL;
    /*
     p_SHORT    xBuf = _NULL;
     p_SHORT    yBuf = _NULL;
     */
    p_SHORT    indBack;
    _INT       iBrkTrace;
    _INT       nWorkPoints;
    _ULONG     ulMemSize;
    _SHORT     qHorda;
    
    _SHORT     retval = SUCCESS;
    
    low_type   low_data;
    
    low_data.buffers[0].ptr = _NULL;
    *pn1stWrdPoints = *pnLastWrdPoints = nFrLinePoints = 0;
    
    if  ( bIncrementalBreak )
    {
    }
    else
        HWRMemSet( &wbd, 0, sizeof(wbd) );
    
    wbd.bIncrementalBreak = bIncrementalBreak;
    
    iLastPoint = nPoints - 1;
    iFirstPoint = brk_right_trace(trace, 0, iLastPoint);
    nWorkPoints = iLastPoint - iFirstPoint + 1;
    if  ( nWorkPoints < MIN_POINTS_TO_WORDBREAK )
        return  SUCCESS;
    
    /*  Prepare working arrays: */
    
    low_data.nLenXYBuf = MaxPointsGrown(
#ifdef  FORMULA
                                        &trace[iFirstPoint],,
#endif  /*FORMULA*/
                                        (_SHORT)nWorkPoints);
    ulMemSize = (_ULONG)sizeof(_SHORT) * low_data.nLenXYBuf;
    
    if  (   (low_data.xBuf=(p_SHORT)HWRMemoryAlloc(ulMemSize)) == _NULL
         || (low_data.yBuf=(p_SHORT)HWRMemoryAlloc(ulMemSize)) == _NULL )
    {
        goto  RETURN_UNSUCCESS;
    }
    trace_to_xy (low_data.xBuf,low_data.yBuf,nWorkPoints,&trace[iFirstPoint]);
    ScaleTrj (low_data.xBuf,low_data.yBuf,nWorkPoints);   /* To Std. size */
    
    /*  Find scaling ranges: */
    
    if  ( ! FindXYScale (low_data.xBuf,low_data.yBuf,
                         0,
                         nWorkPoints-1,
                         &wbd) )
    {
        *pn1stWrdPoints = *pnLastWrdPoints = 0; /* i.e. no wordbreaking made */
        goto  RETURN_SUCCESS;
    }
    
    /*  Breaking text to words begins: */
    
    if  ( !AllocLowDataBuffers(&low_data) )
    {
        goto  RETURN_UNSUCCESS;
    }
    
    /*  Filter trace: */
    
    qHorda = ONE_FOURTH(wbd.xRange);
    if  ( qHorda < SQRT_ALEF )
    {
        if  ( qHorda < 3 )
            qHorda = 3*3;
        else
            qHorda *= qHorda;
    }
    else
        qHorda = ALEF;
    
    if  ( !GetFilteredData (qHorda, &low_data,
                            &xArray,
                            &yArray,
                            &nWorkPoints) )
    {
        goto  RETURN_UNSUCCESS;
    }
    
    DBG_GET_DEMO_BORDERS(nWorkPoints)
    DBG_SHOW_TRACE
    
    nStrokes = FindAllStrokes( xArray, yArray, nWorkPoints, strk, MAX_STR, &wbd );
    if  ( nStrokes <= 1 )
        goto  RETURN_SUCCESS;
    
    indBack = low_data.buffers[2].ptr;
    iBrkTrace = indBack[strk[0].iEnd+1] + iFirstPoint;
    
    if  ( trace[iBrkTrace].y == BREAK
         && ( trace[iBrkTrace+1].y == BREAK || trace[iBrkTrace-1].y == BREAK ) )
    {
        /*  The wordbreak was put on the prev. pass of "FrmWordBreak", */
        /* so now we should only assign return values and exit:        */
        *pn1stWrdPoints = strk[0].iEnd + 2;
        goto  RETURN_SUCCESS;
    }
    
    if  ( ! ExtractWords (xArray,yArray,
                          strk,nStrokes,
                          &wbd,&bWasBroken,
                          pn1stWrdPoints,&nFrLinePoints) )
    {
        goto  RETURN_UNSUCCESS;
    }
    
RETURN_SUCCESS:;
    /*  If trace (xArray,yArray) was filtered, then get correct */
    /* points numbers:                                          */
    if  ( low_data.buffers[0].ptr != _NULL )
    {
        /*   Transform (*pn1stWrdPoints) and nFrLinePoints so     */
        /* that they be in terms of source (non-filtered) points' */
        /* numbers:                                               */
        UnfilterAnswer( &low_data, pn1stWrdPoints, &nFrLinePoints,
                       &trace[iFirstPoint] );
        *pnLastWrdPoints = nFrLinePoints;
        /*
         if  ( nFrLinePoints == 0 )
         *pnLastWrdPoints = 0;
         else  {
         _INT    n1stWrdAndFrLine = *pn1stWrdPoints + nFrLinePoints - 1;
         if  ( n1stWrdAndFrLine == nPoints )
         *pnLastWrdPoints = nFrLinePoints;
         else
         *pnLastWrdPoints = nPoints - n1stWrdAndFrLine + 1;
         }
         */
    }
    retval = SUCCESS;
    goto  FREE_BUFFERS;
    
RETURN_UNSUCCESS:;
    retval = UNSUCCESS;
    
FREE_BUFFERS:;
    DeallocLowDataBuffers (&low_data);
    if  ( low_data.yBuf )  HWRMemoryFree (low_data.yBuf);
    if  ( low_data.xBuf )  HWRMemoryFree (low_data.xBuf);
    
    return  retval;
    
} /*FrmWordBreak*/
/***************************************/


_BOOL  AllocLowDataBuffers ( low_type _PTR pLowData )
{
    _INT      i;
    p_SHORT   pBuffers;
    
    if  ( (pBuffers=(p_SHORT)HWRMemoryAlloc(sizeof(_SHORT)*LOWBUF*4)) == _NULL )
    {
        return  _FALSE;
    }
    
    pLowData->buffers[0].ptr = pBuffers;
    pLowData->buffers[1].ptr = pBuffers + LOWBUF;
    pLowData->buffers[2].ptr = pBuffers + LOWBUF*2;
    pLowData->buffers[3].ptr = pBuffers + LOWBUF*3;
    
    pLowData->buffers[0].nSize =
    pLowData->buffers[1].nSize =
    pLowData->buffers[2].nSize =
    pLowData->buffers[3].nSize = LOWBUF;
    
    pBuffers = pLowData->buffers[3].ptr;
    for  ( i=0;  i<LOWBUF;  i++ )
    {
        pBuffers[i] = i;
    }
    
    return  _TRUE;
    
} /*AllocLowDataBuffers*/
/***************************************/


_VOID  DeallocLowDataBuffers ( low_type _PTR pLowData )
{
    
    if  ( pLowData->buffers[0].ptr != _NULL )
    {
        HWRMemoryFree (pLowData->buffers[0].ptr);
        pLowData->buffers[0].ptr = _NULL;
    }
    
} /*DeallocLowDataBuffers*/
/***************************************/


_BOOL  GetFilteredData ( _SHORT qhorda_filt,
                        low_type _PTR pLowData,
                        p_SHORT _PTR pxArray,
                        p_SHORT _PTR pyArray,
                        p_INT pnPoints)
{
    
    /* Pack data for filter: */
    
    SetXYToInitial(pLowData);
    pLowData->ii = *pnPoints;
    
    /* Filter word: */
    
    Errorprov (pLowData);
    if  ( PreFilt (qhorda_filt,pLowData) != SUCCESS )
        return  _FALSE;
    
    {     /* Swap buffers with back indexes: */
        BUF_DESCR bufTmp = pLowData->buffers[2];
        pLowData->buffers[2] = pLowData->buffers[3];
        pLowData->buffers[3] = bufTmp;
    }
    
    if  ( Filt ( pLowData, qhorda_filt,  NOABSENCE ) != SUCCESS )
        return  _FALSE ;
    
    /* Unpack filter data: */
    
    *pnPoints = pLowData->ii;
    *pxArray = pLowData->x;
    *pyArray = pLowData->y;
    
    return  _TRUE;
    
} /*GetFilteredData*/
/***************************************/


_SHORT  FindAllStrokes ( p_SHORT xArray, p_SHORT yArray, _INT nPoints,
                        p_STRK strk, _INT nMaxStrokes,
                        p_WORDBRKDATA pwbd )
{
    _INT    nStrokes;
    _INT    iLastPoint = nPoints-1;
    _LONG   ldxSum, ldySum;
    _INT    nStrkSum;
    _INT    i;
    
    for  ( i=0, nStrokes=nStrkSum=0,  ldxSum=ldySum=0L; i<iLastPoint; i++ )
    {
        if  ( yArray[i] == BREAK )
        {
            if  ( (i = nobrk_right(yArray,i,iLastPoint)) > iLastPoint )
                break;
            strk->iBeg = i;
            i = brk_right(yArray,i,iLastPoint) - 1;
            strk->iEnd = i;
            GetTraceBox (xArray,yArray, strk->iBeg, strk->iEnd, &strk->box);
            strk->xMean = Xmean_range(xArray,yArray, strk->iBeg, strk->iEnd);
            strk->yMean = Ymean_range(yArray, strk->iBeg, strk->iEnd);
            strk->bMayBeFrline = MayBeFrline(xArray,yArray, strk->iBeg, strk->iEnd, pwbd->xRange);
            if  ( ! strk->bMayBeFrline )
            {
                ldxSum += DX_STRK(strk);
                ldySum += DY_STRK(strk);
                nStrkSum++;
            }
            strk->bIsWordEnd = _FALSE;
            strk->iStrk = nStrokes;
            if  ( (++nStrokes) >= nMaxStrokes )
                break;
            strk++;
        }
    }
    
    if  ( nStrkSum > 0 )  {
        pwbd->dxNoFrline = ONE_NTH( ldxSum, nStrkSum );
        pwbd->dyNoFrline = ONE_NTH( ldySum, nStrkSum );
    }
    else  {
        pwbd->dxNoFrline = THREE(pwbd->xRange);
        pwbd->dyNoFrline = TWO(pwbd->yRange);
    }
    
    return  nStrokes;
    
} /*FindAllStrokes*/
/***************************************/


_BOOL  FindXYScale ( p_SHORT xArray, p_SHORT yArray, _INT iFirst, _INT iLast, p_WORDBRKDATA pwbd )
{
    _LONG     ldxSumIncr, ldySumIncr;
    _LONG     lNumIncrIntervals;
    _INT      nMaxRectRatio;
    
    
    for  ( nMaxRectRatio=MAX_RECTANGLE;
          nMaxRectRatio<MAX_RECTANGLE*4;
          nMaxRectRatio += 2 )
    { /*5*/
        
        if  ( delta_interval(xArray,yArray,
                             iFirst,iLast,
                             nMaxRectRatio, 0,
                             &ldxSumIncr,&ldySumIncr,&lNumIncrIntervals,_FALSE)
             )
        {
            lNumIncrIntervals += pwbd->lNumIntervals;
            if  ( lNumIncrIntervals >= MIN_INTERVALS_TO_RELY_ON )
            {
                pwbd->ldxSum += ldxSumIncr;
                pwbd->ldySum += ldySumIncr;
                pwbd->lNumIntervals = lNumIncrIntervals;
                break;
            }
        }
        
    } /*5*/
    
    if  ( pwbd->lNumIntervals < MIN_INTERVALS_TO_RELY_ON )
        return  _FALSE;
    
    pwbd->xRange = ONE_NTH( pwbd->ldxSum, pwbd->lNumIntervals );
    pwbd->yRange = ONE_NTH( pwbd->ldySum, pwbd->lNumIntervals );
    
    /*  Adjust scale ranges according to Formula Project */
    /* experience:                                       */
    
    pwbd->xRange = TWO_THIRD(pwbd->xRange);
    pwbd->yRange = FOUR_THIRD(pwbd->yRange);
    
    /*  "Forget" about the oldest intervals: */
    
    if  ( pwbd->lNumIntervals > MAX_INTERVALS_TO_STORE )
    {
        _INT    nDiv = (_INT)ONE_NTH(pwbd->lNumIntervals,2*MIN_INTERVALS_TO_RELY_ON);
        if  ( nDiv > 0 )
        {
            pwbd->ldxSum /= nDiv;
            pwbd->ldySum /= nDiv;
            pwbd->lNumIntervals /= nDiv;
        }
    }
    
    return  _TRUE;
    
} /*FindXYScale*/
/***********************************************/

/*  The function finds fraction lines and grouped parts of */
/* points in trace (xArray&yArray) from point #iFirst to   */
/* point #iLast:                                           */

_BOOL  ExtractWords ( p_SHORT xArray, p_SHORT yArray,
                     p_STRK strk, _INT nStrokes,
                     p_WORDBRKDATA pwbd, p_BOOL pbWasBroken,
                     p_SHORT pn1stWrdPoints,
                     p_SHORT pnFrLinePoints )
{
    _INT    iStrk;
    _BOOL   bWordFound, bSingleStrokeWord;
    _BOOL   bFrstStrk, bLastStrk;
    p_STRK  strkCur, strkPrev;
    
    
    for  ( iStrk=0, strkCur=strk, strkPrev=_NULL; iStrk<nStrokes; iStrk++, strkPrev=strkCur++ )
    {
        bFrstStrk = (iStrk==0);
        bLastStrk = (iStrk==nStrokes-1);
        
        DBG_SET_CURSTROKE
        DBG_SHOW_NEXT_STROKE
        
        bWordFound = _FALSE;
        bSingleStrokeWord = _FALSE;
        
        /*  Frline should meet some conditions (see comment at */
        /* "MayBeFrline" function):                            */
        
        if  ( IsFrline(xArray,yArray,strkCur,bFrstStrk,bLastStrk,pwbd) )
        {
            bWordFound = _TRUE;
            bSingleStrokeWord = _TRUE;
        }
        
#if  !FRLINE_ONLY
        else  if ( !bFrstStrk )
        {
            if ( StrokesFarEnough(xArray,yArray,strkPrev,strkCur,pwbd)
                 || CurUnderWord(xArray,yArray,strkCur,pwbd) )
            {
                bWordFound = _TRUE;
            }
        }
#endif
        
        
        if  ( bWordFound )
        { /*60*/   /*  Here we have the new Frline: */
            
            DBG_SHOW_WORDBEG
            
            *pbWasBroken = _TRUE;
            if  ( !bFrstStrk )
                strkPrev->bIsWordEnd = _TRUE;
            if  ( bSingleStrokeWord )
            {
                strkCur->bIsWordEnd = _TRUE;
                *pnFrLinePoints = strkCur->iEnd - strkCur->iBeg + 1;
            }
            else
                *pnFrLinePoints = 0;
            
            /*  Prepare return values depending on the working */
            /* mode:                                           */
            
            if  ( pwbd->bIncrementalBreak )
            {
                if  ( bFrstStrk )
                {
                    *pn1stWrdPoints = strkCur->iEnd + 2;
                    *pnFrLinePoints = 0;
                }
                else
                    *pn1stWrdPoints = strkCur->iBeg;
                
                return  _TRUE;
            }
            else  /* !incremental break*/
            {
                if  ( !bFrstStrk )
                    (strkCur-1)->bIsWordEnd = _TRUE;
            }
            
        } /*60*/
        
    } /*20*/
    
    return  _TRUE;
    
} /*ExtractWords*/
/***********************************************/


_VOID  UnfilterAnswer ( low_type _PTR pLowData,
                       p_SHORT pn1stWordPoints,
                       p_SHORT pnFrLinePoints,
                       _TRACE  trace )
{
    p_SHORT  indBack = pLowData->buffers[2].ptr;
    
#if  0
    _INT   iStrk;
    _INT   iEndStrk;
    
    /*  Set 2-BREAKs between words: */
    
    for  ( iStrk=0;  iStrk<nStrokes-1;  iStrk++ )
    {
        if  ( (iEndStrk=strk[iStrk].iEnd) <= *pn1stWordPoints )
            continue;
        if  ( strk[iStrk].bIsWordEnd )
        {
            iEndStrk = indBack[iEndStrk+1] - 1;
            if  ( trace[iEndStrk+2].y != BREAK )
            {
                trace[iEndStrk-1].x = trace[iEndStrk].x;
                trace[iEndStrk-1].y = trace[iEndStrk].y;
                trace[iEndStrk].x = 0;
                trace[iEndStrk].y = BREAK;
            }
        }
    }
#endif /*0*/
    
    if  ( *pnFrLinePoints != 0 )
    {
        *pnFrLinePoints = indBack[*pn1stWordPoints + *pnFrLinePoints];
    }
    *pn1stWordPoints = indBack[*pn1stWordPoints];
    if  ( *pnFrLinePoints != 0 )
        *pnFrLinePoints -= *pn1stWordPoints - 2;
    
} /*UnfilterAnswer*/
/***********************************************/

/*********************************************************************/
/*   End of the main upper-level functions.                          */
/*   The following lines are "bricks" - relatively small utilities   */
/* for particular analyses.                                          */
/*********************************************************************/

_BOOL  IsFrline ( p_SHORT xArray, p_SHORT yArray,
                 p_STRK strkCur,
                 _BOOL bFrstStrk, _BOOL bLastStrk,
                 p_WORDBRKDATA pwbd )
{
    _SHORT  dxStrkCur;
    
    
    if  ( !strkCur->bMayBeFrline )
    {
        return  _FALSE;
    }
    
    if  ( (strkCur->iEnd - strkCur->iBeg) < MIN_POINTS_IN_FRLINE-1 )
    {
        return  _FALSE;
    }
    
    if  (   !bFrstStrk
         && chk_sign(xArray,yArray,
                     (strkCur-1)->iBeg,strkCur->iEnd,
                     ONE_HALF(pwbd->xRange),FOUR_THIRD(pwbd->yRange))
         && (   bLastStrk
             || !SoftInRect( &strkCur->box, &(strkCur+1)->box, !STRICT_IN )
             )
         )
    {
        if  (   strkCur->iStrk >= 2 && SoftInRect( &(strkCur-1)->box, &(strkCur-2)->box, !STRICT_IN ))
        {
            /*nothing*/
        }
        else
        {
            return  _FALSE;
        }
    }
    
    if  (   !bLastStrk
         && chk_sign(xArray,yArray,
                     strkCur->iBeg,(strkCur+1)->iEnd,
                     ONE_HALF(pwbd->xRange),FOUR_THIRD(pwbd->yRange))
         && (   bFrstStrk
             || !SoftInRect( &strkCur->box, &(strkCur-1)->box, !STRICT_IN )
             )
         )
    {
        return  _FALSE;
    }
    
    dxStrkCur = DX_STRK(strkCur);
    
    if  (   dxStrkCur > pwbd->xRange * 16
         || (   dxStrkCur > TWO(pwbd->dxNoFrline)
             && dxStrkCur > FIVE(pwbd->xRange)
             )
         )
    {
        return  _TRUE;
    }
    
    else
    { /*40*/
        
        /*  Additional considerations are needed for small */
        /* horiz. lines:                                   */
        
        if  (   !bFrstStrk  &&  !bLastStrk && HorizLine_AmongNmrAndDnmr(xArray,yArray,strkCur,pwbd) )
        {
            return  _TRUE;
        }
        if  (   (   !bFrstStrk
                 && Minus_NearWord(strkCur,strkCur-1,pwbd)
                 && (   bLastStrk
                     || Minus_NearWord(strkCur,strkCur+1,pwbd)
                     )
                 )
             || (   !bLastStrk
                 && Minus_NearWord(strkCur,strkCur+1,pwbd)
                 && (   bFrstStrk
                     || (   !HardOverlapRect(&strkCur->box,&(strkCur-1)->box,
                                             !STRICT_OVERLAP)
                         && HWRAbs(strkCur->xMean - (strkCur-1)->xMean)
                         > TWO(pwbd->xRange)
                         )
                     )
                 )
             )
        {
            return  _TRUE;
        }
    } /*40*/
    
    return  _FALSE;
    
} /*IsFrline*/
/***********************************************/

#if  !FRLINE_ONLY

_BOOL  StrokesFarEnough ( p_SHORT xArray, p_SHORT yArray, p_STRK pStrk1, p_STRK pStrk2, p_WORDBRKDATA pwbd )
{
    _SHORT  dxFarEnough;
    _SHORT  dyFarEnough;
    _SHORT  dxAbsMean, dyAbsMean;
    _SHORT  dxMin, dxMax;
    _INT    iBeg1 = pStrk1->iBeg;
    _INT    iEnd1 = pStrk1->iEnd;
    _INT    iBeg2 = pStrk2->iBeg;
    _INT    iEnd2 = pStrk2->iEnd;
    
    /*  If the two strokes constitute some sign */
    /* then do not check for distantness:       */
    
    if  ( pStrk1==pStrk2+1  ||  pStrk2==pStrk1+1 )
    { /* for adjacent strokes */
        _INT    iBeg = HWRMin(iBeg1,iBeg2);
        _INT    iEnd = HWRMax(iEnd1,iEnd2);
        if  ( chk_sign(xArray,yArray,iBeg,iEnd, ONE_HALF(pwbd->xRange),FOUR_THIRD(pwbd->yRange)) )
        {
            return  _FALSE;
        }
    }
    
    /*  Check if the strokes boxes are far enough: */
    
    dxFarEnough = pwbd->xRange * 6;
    dyFarEnough = pwbd->yRange;
    
    if  ( (pStrk1->bMayBeFrline || pStrk2->bMayBeFrline)
         && xHardOverlapRect( &pStrk1->box, &pStrk2->box, !STRICT_OVERLAP ) )
    {
        TO_TWO_TIMES( dyFarEnough );
    }
    
    if  (   pStrk1->box.left > pStrk2->box.right + dxFarEnough
         || pStrk2->box.left > pStrk1->box.right + dxFarEnough
         || pStrk1->box.top > pStrk2->box.bottom + dyFarEnough
         || pStrk2->box.top > pStrk1->box.bottom + dyFarEnough
         )
    {
        return  _TRUE;
    }
    
    /*  Check if the mean points ("gravity centers") */
    /* of the strokes are far enough:                */
    
    /*  dxFarEnough = pwbd->xRange * 6; */
    dyFarEnough = pwbd->yRange * 4;
    
    dxAbsMean = pStrk1->xMean - pStrk2->xMean;
    TO_ABS_VALUE(dxAbsMean);
    dyAbsMean = HWRAbs(pStrk1->yMean - pStrk2->yMean);
    TO_ABS_VALUE(dyAbsMean);
    
    if  (   dxAbsMean > dxFarEnough || dyAbsMean > dyFarEnough )
    {
        if  ( !is_cross( xArray[iBeg1], yArray[iBeg1],
                        xArray[iEnd1], yArray[iEnd1],
                        xArray[iBeg2], yArray[iBeg2],
                        xArray[iEnd2], yArray[iEnd2] ) )
        {
            return  _TRUE;
        }
    }
    
    /*  Check if the distance between strokes mean points  */
    /* is big compared to their boxes widths.  The strokes */
    /* should be wide enough themselves.                   */
    
    dxMax = DX_STRK( pStrk1 );
    dxMin = DX_STRK( pStrk2 );
    if  ( dxMin > dxMax )
        SWAP_SHORTS( dxMin, dxMax );
    
    if  (   dxMin > pwbd->xRange && dxAbsMean > THREE_HALF(dxMax) )
    {
        return  _TRUE;
    }
    
    return  _FALSE;
    
} /*StrokesFarEnough*/
/***********************************************/


_BOOL  CurUnderWord ( p_SHORT xArray, p_SHORT yArray, p_STRK pStrk, p_WORDBRKDATA pwbd )
{
    p_STRK  pStrkPrev  = pStrk-1;
    _SHORT  dxStrk     = DX_STRK(pStrk);
    _SHORT  dyStrk     = DY_STRK(pStrk);
    _SHORT  dxStrkPrev = DX_STRK(pStrkPrev);
    _SHORT  dyStrkPrev = DY_STRK(pStrkPrev);
    _SHORT  xRange     = pwbd->xRange;
    _SHORT  yRange     = pwbd->yRange;
    _SHORT  xRange2    = ONE_HALF(xRange);
    _SHORT  yRange2    = ONE_HALF(yRange);
    
    
    /* pStrk must be non-first one: */
    
    if  (   ((dxStrk > xRange)      &&  (dyStrk > THREE_HALF(yRange))    )
         || ((dxStrkPrev > xRange)  &&  (dyStrkPrev > THREE_HALF(yRange)))
         || (   dxStrk > xRange2
             && dyStrk > yRange2
             && dxStrkPrev > xRange2
             && dyStrkPrev > yRange2 ))
    { /*10*/
        
        _SHORT  dxEndBeg = xArray[pStrk->iBeg] - xArray[pStrkPrev->iEnd];
        _SHORT  dyEndBeg = yArray[pStrk->iBeg] - yArray[pStrkPrev->iEnd];
        
        /*  Divide if the beg of the stroke is far enough from */
        /* the end of the prev. one:                           */
        
        if  (   ONE_FIFTH(HWRAbs(dxEndBeg)) > xRange
             && (!pStrk->bMayBeFrline || HWRAbs(dyEndBeg) > TWO(yRange) ) )
        {
            return  _TRUE;
        }
        
        if  ( dxStrk==0 || dyStrk==0 || dxStrkPrev==0 || dyStrkPrev==0 )
            return  _FALSE;
        
        /*  Divide if the boxes don't intersect or do it */
        /* slightly and the mean points of the strokes   */
        /* are y-far enough:                             */
        
        if  (   !pStrk->bMayBeFrline
             && !pStrkPrev->bMayBeFrline
             && !yHardOverlapRect(&pStrk->box,&pStrkPrev->box,STRICT_OVERLAP)
             && !is_cross( xArray[pStrk->iBeg], yArray[pStrk->iBeg],
                          xArray[pStrk->iEnd], yArray[pStrk->iEnd],
                          xArray[pStrkPrev->iBeg], yArray[pStrkPrev->iBeg],
                          xArray[pStrkPrev->iEnd], yArray[pStrkPrev->iEnd] )
             )
        {
            if  (   pStrk->box.top > pStrkPrev->box.bottom
                 || pStrkPrev->box.top > pStrk->box.bottom
                 || !chk_slash( xArray, yArray, pStrk->iBeg, pStrk->iEnd,
                               yRange2, !WHOLE_TRAJECTORY )
                 )
            {
                _INT    nStrkRatio = (dxStrk>dyStrk)? (TWO(dxStrk) / dyStrk) :(TWO(dyStrk) / dxStrk);
                _INT    nPrevRatio = (dxStrkPrev>dyStrkPrev)? (TWO(dxStrkPrev) / dyStrkPrev) :(TWO(dyStrkPrev) / dxStrkPrev);
                _SHORT  dyMeanMax  = yRange;
                
                if  ( nStrkRatio<=3  &&  nPrevRatio<=3 )   /*  Straight vert. or */
                    dyMeanMax = TWO(dyMeanMax);              /* hor. parts may be  */
                /* more far in letter.*/
                
                if  ( HWRAbs(pStrk->yMean - pStrkPrev->yMean) > dyMeanMax )
                {
                    return  _TRUE;
                }
            }
        }
        
    } /*10*/
    
    return  _FALSE;
    
} /*CurUnderWord*/

#endif  /*!FRLINE_ONLY*/
/***********************************************/


_BOOL  HorizLine_AmongNmrAndDnmr ( p_SHORT xArray, p_SHORT yArray, p_STRK strk, p_WORDBRKDATA pwbd )
{
    p_STRK  strkPrev = (strk - 1);
    p_STRK  strkNext = (strk + 1);
    
    if  (
         /*  !(strk-1)->bMayBeFrline
          &&
          */
         UpperStroke(yArray,
                     strkPrev->iBeg,strkPrev->iEnd,
                     strk->iBeg,strk->iEnd,
                     pwbd->yRange)
         /*   && DX_STRK(strk+1) <= TWO(DX_STRK(strk)) */
         && AboveStroke(xArray,yArray,
                        strk->iBeg,strk->iEnd,
                        strkNext->iBeg,strkNext->iEnd,
                        TWO(pwbd->xRange),
                        pwbd->yRange)
         && (!strkNext->bMayBeFrline || DX_STRK(strkNext) < DX_STRK(strk) ) )
    {
        return  _TRUE;
    }
    
    return  _FALSE;
    
} /*HorizLine_AmongNmrAndDnmr*/
/***********************************************/


_BOOL  Minus_NearWord ( p_STRK strkFrl, p_STRK strkNear, p_WORDBRKDATA pwbd )
{
    _SHORT  xChkRange;
    _SHORT  xMeanNear;
    _SHORT  dxMinFromNear;
    
    /*
     if  ( !yHardOverlapRect(&strkNear->box,&strkFrl->box,!STRICT_OVERLAP) )
     return  _FALSE;
     */
    if  ( strkNear->bMayBeFrline || HardOverlapRect(&strkNear->box,&strkFrl->box,!STRICT_OVERLAP) )
        return  _FALSE;
    
    xChkRange = pwbd->xRange * 12;
    
    if  (DX_STRK(strkFrl) > xChkRange )
    {
        return  _TRUE;
    }
    
    xMeanNear = strkNear->xMean;
    dxMinFromNear = ONE_HALF(DX_STRK(strkFrl));
    
    if  ((strkFrl->box.left - xMeanNear) > dxMinFromNear
         || (xMeanNear - strkFrl->box.right) > dxMinFromNear )
    {
        return  _TRUE;
    }
    
    return  _FALSE;
    
} /*Minus_NearWord*/

/***********************************************/

#ifdef  QAQAQA  /*i.e. exclude from compilation */

_BOOL  chk_horiz_move ( p_SHORT xArray,  p_SHORT yArray, _INT iLeft,  _INT iRight )
{
    _INT     i;
    _SHORT   dxLeftToRight, dyAllowed;
    _SHORT   dxBad;
    _SHORT   yUp, yDown;
    _BOOL    bMoveToRight;
    
    
    if  (   (iLeft=nobrk_right(yArray,iLeft,iRight)) > iRight
         || (iRight=nobrk_left(yArray,iRight,iLeft)) < iLeft
         || brk_right(yArray,iLeft,iRight) <= iRight
         || iLeft >= iRight-2 )
    {
        return  _FALSE;
    }
    dxLeftToRight = (xArray[iRight] - xArray[iLeft]);
    
    if  ( dxLeftToRight > 0 )
        bMoveToRight = _TRUE;
    else  {
        bMoveToRight = _FALSE;
        dxLeftToRight = -dxLeftToRight;
    }
    dyAllowed = ONE_HALF(dxLeftToRight);
    
    for  ( i=iLeft+2, dxBad=0, yUp=yDown=yArray[iLeft]; i<=iRight; i++ )
    { /*20*/
        
        if  ( yArray[i] > yDown )
            yDown = yArray[i];
        else  if  ( yArray[i] < yUp )
            yUp = yArray[i];
        
        if  ( bMoveToRight )
        {
            if  ( xArray[i] < xArray[i-1] - 1 || xArray[i] < xArray[i-2] )
                dxBad += xArray[i-1] - xArray[i];
        }
        else
        {
            if ( xArray[i] > xArray[i-1] + 1 || xArray[i] > xArray[i-2] )
                dxBad += xArray[i] - xArray[i-1];
        }
        
    } /*20*/
    
    if  ((yDown - yUp) > dyAllowed|| dxBad > ONE_FOURTH(dxLeftToRight))
        return  _FALSE;
    
    return  _TRUE;
    
} /*chk_horiz_move*/

#endif  /*QAQAQA*/

/**************************************/


_BOOL  UpperStroke ( p_SHORT yArray,
                    _INT left1, _INT right1,
                    _INT left2, _INT right2,
                    _SHORT yTolerance )
{
    
    return  ((Ymean_range(yArray,left1,right1)< Ymean_range(yArray,left2,right2) - yTolerance)
             || (Ydown_range(yArray,left1,right1)< Yup_range(yArray,left2,right2)));
    
} /*UpperStroke*/


_BOOL    AboveStroke ( p_SHORT xArray, p_SHORT yArray,
                      _INT left1, _INT right1,
                      _INT left2, _INT right2,
                      _SHORT xTolerance,
                      _SHORT yTolerance )
{
    _SHORT  xMin1, xMax1;
    _SHORT  xMin2, xMax2;
    
    xMinMax(left1,right1,xArray,yArray, &xMin1,&xMax1);
    xMinMax(left2,right2,xArray,yArray, &xMin2,&xMax2);
    
    return  (xMin1 < xMax2 + xTolerance
             && xMin2 < xMax1 + xTolerance
             && UpperStroke (yArray,left1,right1,left2,right2,yTolerance) );
    
} /*AboveStroke*/
/***********************************************/

#define  MIN_COORD  100
#define  MAX_COORD  7000

#define  SCALE_COORD(xy)  (TWO(xy))

_VOID  ScaleTrj ( p_SHORT x, p_SHORT y, _INT nPoints)
{
    _INT  i;
    
    for  ( i=0;  i<nPoints;  i++ )
    {
        if  ( y[i] != BREAK )
        {
            x[i] = SCALE_COORD(x[i]);
            y[i] = SCALE_COORD(y[i]);
        }
    }
    
#ifdef  QAQAQA
    _INT    i;
    _INT    nToMult,  nToDiv;
    _INT    dnMax;
    _RECT   box;
    
    GetTraceBox (x,y,0,nPoints-1,&box);
    
    dnMax = max( DX_RECT(box), DY_RECT(box) );
    
    if  ( dnMax > MAX_COORD )
    {
        nToMult = 1;
        nToDiv = ONE_NTH(dnMax,MAX_COORD);
    }
    else
    {
        nToMult = ONE_NTH(MAX_COORD,dnMax);
        nToDiv = 1;
    }
    
    if  ( nToMult>1 || nToDiv>1 )
    {
        for  ( i=0;  i<nPoints;  i++ )
        {
            if  ( y[i] == BREAK )
                continue;
            x[i] = MIN_COORD + ONE_NTH((x[i]-box.left)*nToMult, nToDiv);
            y[i] = MIN_COORD + ONE_NTH((y[i]-box.top)*nToMult, nToDiv);
        }
    }
#endif /*QAQAQA*/
    
} /*ScaleTrj*/

#undef  MAX_COORD
#undef  MIN_COORD
/***********************************************/

#endif /*USE_FRM_WORD*/

/***********************************************/
/*   Here are the functions used in the other  */
/* modules:                                    */
/***********************************************/

/*  This routine corresponds to "chk_frline" from FRM.    */

/* Frline shouldn't be too small.                         */
/*  Frline should be rather straight mostly in horiz. di- */
/* rection, it shouldn't be too bended and it's curvity   */
/* sign shouldn't change more than 1 time (if the curvity */
/* has substantial abs. value):                           */

#define  FRLINE_STEPS  24

_BOOL  MayBeFrline ( p_SHORT xArray, p_SHORT yArray, _INT iBeg, _INT iEnd, _SHORT  xRange )
{
    _INT     i;
    _SHORT   dxLeftToRight, dyLeftToRight;
    _SHORT   dyAllowed, dyCurvity;
    _SHORT   dxCur, dyFromStraight, dxAbs;
    _LONG    lMultCoef, lDivCoef, lDivCoef2;
    _BOOL    bWasWrong;
    _BOOL    bPhase;
    _INT     nStep;
    _INT     nCurvChanges, nCurvSign;
    
    
    if  ((iBeg=nobrk_right(yArray,iBeg,iEnd)) >= iEnd || (iEnd=nobrk_left(yArray,iEnd,iBeg)) <= iBeg )
        return  _FALSE;
    
    if  ( brk_right(yArray,iBeg,iEnd) < iEnd )
        return  _FALSE;   /* i.e. there is break within this trj. */
    
    FindStraightPart (xArray,yArray,&iBeg,&iEnd);
    /*   Check the straightness and horizontalness  */
    /* of the part between "iBeg" and "iEnd":       */
    
    dxLeftToRight = xArray[iEnd] - xArray[iBeg];
    dyLeftToRight = yArray[iEnd] - yArray[iBeg];
    
    if  ((dxAbs=(_SHORT)HWRAbs(dxLeftToRight)) >= xRange && dxAbs > TWO(HWRAbs(dyLeftToRight)) )
    {
        dyAllowed = (_SHORT)ONE_NTH(dxAbs+ONE_HALF(HWRAbs(dyLeftToRight)), 6);
        if  ( dyAllowed < 2 )
            dyAllowed = 2;
        
        dyCurvity = (_SHORT)ONE_HALF(dyAllowed);
        
        lMultCoef = dyLeftToRight;
        lDivCoef = dxLeftToRight;
        
        lDivCoef2 = ONE_HALF(lDivCoef);
        if  ( !EQ_SIGN(lMultCoef,lDivCoef) )
            lDivCoef2 = -lDivCoef2;
        
        if  ( lDivCoef == 0 )
            return  _FALSE;
        
        nStep = ONE_NTH(iEnd-iBeg, FRLINE_STEPS);
        if  ( nStep <= 0 )
            nStep = 1;
        
        for  ( i=iBeg+nStep,
              bWasWrong    = _FALSE,
              bPhase       = _FALSE,
              nCurvSign    = 0,
              nCurvChanges = 0;
              i<=iEnd;
              i+=nStep, bPhase=!bPhase )
        { /*10*/
            
            dyFromStraight = yArray[i] - (  yArray[iBeg]
                                        + (_SHORT)(  (  ((_LONG)(xArray[i]-xArray[iBeg])) * lMultCoef + lDivCoef2 ) / lDivCoef));
            
            if  ( HWRAbs(dyFromStraight) > dyAllowed )
                return  _FALSE;
            
            if  ( bPhase )
            { /*20*/
                
                if  ( dyFromStraight != 0
                     && ( nCurvSign == 0 || !EQ_SIGN(dyFromStraight,nCurvSign) )
                     && HWRAbs(dyFromStraight) > dyCurvity )
                {
                    if  ( ++nCurvChanges > 1 )
                        return  _FALSE;
                    nCurvSign = dyFromStraight;
                }
                
                if  ( ( ((dxCur=xArray[i]-xArray[i-TWO(nStep)]) >= 0)
                         && (dxLeftToRight < 0)) || ( (dxCur <= 0)&& (dxLeftToRight > 0)))
                {
                    if  ( bWasWrong )
                        return  _FALSE;
                    else
                        bWasWrong = _TRUE;
                }
                else
                    bWasWrong = _FALSE;
                
            } /*20*/
            
        } /*10*/
        
        return  _TRUE;
        
    } /*1*/
    
    return  _FALSE;
    
} /*MayBeFrline*/

#undef  FRLINE_STEPS

/***********************************************/

/*  This routine corresponds to "chk_sign" from FRM.    */

_SHORT  chk_sign ( p_SHORT xArray, p_SHORT yArray, _INT iBeg, _INT iEnd, _SHORT xRange, _SHORT yRange )
{
    _INT     iBrkFrst, iBrkLast, iRight2;
    _SHORT   x1,y1, x2, x3,y3, x4;
    _SHORT   yRangeSmallSlash;
    _INT     nPoints1_4, nPoints2_4;
    _INT     iLeftPart1, iRightPart1;
    _INT     iLeftPart2, iRightPart2;
    
    
    if  (   (iBeg=nobrk_right(yArray,iBeg,iEnd)) > iEnd || (iEnd=nobrk_left(yArray,iEnd,iBeg)) < iBeg )
        return  SGN_NOT_SIGN;
    
    if  ( iBeg < (iRight2=iEnd-2) )
    {
        /*10*/
        
        /*  "iBrkFrst" and "iBrkLast" represent to ends of the */
        /* same BREAKs sequence:                               */
        
        if  (   (iBrkFrst = brk_right(yArray,iBeg+1,iRight2)) > iRight2
             || (iBrkLast = nobrk_right(yArray,iBrkFrst,iRight2) - 1) > iRight2-1 )
        {
            return  SGN_NOT_SIGN;
        }
        
        if  ( brk_right(yArray,iBrkLast+1,iEnd) <= iEnd )
            return  SGN_NOT_SIGN;     /* more than two strokes */
        
        nPoints1_4 = (iBrkFrst - iBeg + 1) / 6;
        nPoints2_4 = (iEnd - iBrkLast + 1) / 6;
        
        iLeftPart1 = iBeg + nPoints1_4;
        iRightPart1 = iBrkFrst - 1 - nPoints1_4;
        if  ( iRightPart1 <= iLeftPart1 )
        {
            iLeftPart1 = iBeg;
            iRightPart1 = iBrkFrst - 1;
        }
        iLeftPart2 = iBrkLast + 1 + nPoints2_4;
        iRightPart2 = iEnd - nPoints2_4;
        if  ( iRightPart2 <= iLeftPart2 )
        {
            iLeftPart2 = iBrkLast + 1;
            iRightPart2 = iEnd;
        }
        
        yRangeSmallSlash = (_SHORT)(yRange / 16);
        
        if  ( MayBeFrline (xArray,yArray,iBeg,iBrkFrst,xRange) )  { /*40*/
            
            if  ( is_cross (xArray[iLeftPart1],yArray[iLeftPart1],
                            xArray[iRightPart1],yArray[iRightPart1],
                            xArray[iLeftPart2],yArray[iLeftPart2],
                            xArray[iRightPart2],yArray[iRightPart2]) )
            {
                if  (  chk_slash( xArray, yArray, iBrkLast, iEnd, yRangeSmallSlash, !WHOLE_TRAJECTORY) )
                    return  SGN_PLUS;
                else
                    return  SGN_NOT_SIGN;
            }
            
            else  if  ( MayBeFrline (xArray,yArray,iBrkLast,iEnd,xRange) )
            { /*50*/
                
                x1 = xArray[iBeg];
                x2 = xArray[iBrkFrst-1];
                if  ( x1 > x2 )
                    SWAP_SHORTS(x1,x2);
                
                x3 = xArray[iBrkLast+1];
                x4 = xArray[iEnd];
                if  ( x3 > x4 )
                    SWAP_SHORTS(x3,x4);
                
                y1 = (_SHORT)MEAN_OF(yArray[iBeg], yArray[iBrkFrst-1]);
                y3 = (_SHORT)MEAN_OF(yArray[iBrkLast+1], yArray[iEnd]);
                
                if ( (yRange==0  ||  HWRAbs(y1-y3) < TWO(yRange))
                     && ONE_HALF(HWRAbs(y1-y3)) < HWRMin(HWRAbs(x2-x3), HWRAbs(x4-x1))
                     && x2 > x3
                     && x4 > x1
                     && (x4-x3+2)/3 < (x2-x1)
                     && (x2-x1+2)/3 < (x4-x3)
                     )
                {
                    return  SGN_EQUAL;
                }
                
            } /*50*/
            
        } /*40*/
        
        else  if  ( chk_slash (xArray,yArray, iBeg,iBrkFrst,yRangeSmallSlash, !WHOLE_TRAJECTORY) )
        {
            /*60*/
            if  ( MayBeFrline (xArray,yArray,iBrkLast,iEnd,xRange) )
            {
                if  ( is_cross (xArray[iLeftPart1],yArray[iLeftPart1],
                                xArray[iRightPart1],yArray[iRightPart1],
                                xArray[iLeftPart2],yArray[iLeftPart2],
                                xArray[iRightPart2],yArray[iRightPart2]) )
                {
                    return  SGN_PLUS;
                }
            }
        } /*60*/
        
        /*  Check if two parts lie one under another  */
        /* and there is only one break:               */
        
        if  ( ( xArray[iBrkLast+1] < xArray[iBrkFrst-1] && xArray[iBeg] < xArray[iEnd] )
             || ( xArray[iBrkLast+1] > xArray[iBrkFrst-1] && xArray[iBeg] > xArray[iEnd] )
             /*CHE(checked above) && brk_right(yArray,iBrkLast+1,iEnd) > iEnd */ )
        { /*120*/
            
            _SHORT  dx, dy;
            _RECT   box;
            
            GetTraceBox (xArray,yArray, iBeg,iEnd,&box);
            dx = (_SHORT)DX_RECT(box);
            dy = (_SHORT)DY_RECT(box);
            
            /*  If two parts as a whole are of very small size: */
            
            if  (   dx < xRange && dy < FOUR_THIRD(yRange) )
                return  SGN_SOME;
            
            
            /*  If every part has "dx>dy" and their total size */
            /* is rather small:                                */
            
            if  (   dx < (xRange*6) && dy < THREE_HALF(yRange) )
            {
                /*130*/
                
                GetTraceBox (xArray,yArray, iBeg,iBrkFrst, &box);
                dx = (_SHORT)DX_RECT(box);
                dy = (_SHORT)DY_RECT(box);
                
                if  ( dx > FOUR_THIRD(dy) )
                {
                    GetTraceBox (xArray,yArray, iBrkLast,iEnd, &box);
                    dx = (_SHORT)DX_RECT(box);
                    dy = (_SHORT)DY_RECT(box);
                    
                    if  ( dx > FOUR_THIRD(dy) )
                        return  SGN_SOME;
                }
                
            } /*130*/
            
        } /*120*/
        
    } /*10*/
    
    return  SGN_NOT_SIGN;
    
} /*chk_sign*/
/***********************************************/

_BOOL  chk_slash  (p_SHORT xArray,  p_SHORT yArray,
                   _INT iLeft,  _INT iRight,
                   _SHORT yRange, _BOOL bWholeTrj )
{
    _INT     i;
    _INT     iPrevGood;
    _SHORT   dxtek, dxprev;
    _SHORT   d2xtek, d2xprev;
    _SHORT   dx, dx_eps;
    _SHORT   dy, dytek, dyabs, dy_eps;
    _BOOL    bWasWrong, bWasChg;
    _INT     nStep;
    _INT     phase;
    _LONG    lMultCoef, lDivCoef, lDivCoef2;
    
    if  (   (iLeft=nobrk_right(yArray,iLeft,iRight)) > iRight
         || (iRight=nobrk_left(yArray,iRight,iLeft)) < iLeft )
        return  _FALSE;
    
    if  ( !bWholeTrj )
    {
        _INT    i1st, i2nd;
        _INT    dindRightLeft = ONE_FOURTH(iRight-iLeft);
        /*  Find the meaningful part of trajectory to analyse: */
        FindStraightPart (xArray,yArray,&iLeft,&iRight);
#if  USE_FRM_WORD
        DBG_PUTWIDEPIXEL(iLeft,GREEN);
        DBG_PUTWIDEPIXEL(iRight,GREEN);
#endif /*USE_FRM_WORD*/
        i1st = iYup_range (yArray,iLeft,iRight);
        i2nd = iYdown_range (yArray,iLeft,iRight);
        if  ( i1st > i2nd )
            SWAP_INTS(i1st,i2nd);
        if  ( (i1st-iLeft) < dindRightLeft )
            iLeft = i1st;
        if  ( (iRight-i2nd) < dindRightLeft )
            iRight = i2nd;
#if  USE_FRM_WORD
        DBG_PUTWIDEPIXEL(iLeft,GREEN);
        DBG_PUTWIDEPIXEL(iRight,GREEN);
#endif /*USE_FRM_WORD*/
    }
    
    dy = yArray[iRight] - yArray[iLeft];
    
    dx = xArray[iRight] - xArray[iLeft];
    lMultCoef = (_LONG)dx;
    
    lDivCoef = dy;
    lDivCoef2 = ONE_HALF(lDivCoef);
    
    if  (   (iLeft < (iRight-3))
         && (dyabs=(_SHORT)HWRAbs(dy)) >= yRange   /* Also check for lDivCoef!=0 */
         && THREE_HALF(dyabs) > HWRAbs(dx))
    { /*1*/
        
        dx_eps = (_SHORT)((HWRAbs(dx)) / 16);
        
        dy_eps = (_SHORT)(dyabs / 10);
        if  ( dy_eps < 2 )
            dy_eps = 2;
        
        if  ( brk_right(yArray,iLeft+1,iRight-1) < iRight )  /*i.e. BREAK exists*/
            return  _FALSE;
        
        nStep = (iRight-iLeft+4) / 16;
        if  ( nStep <= 0 )
            nStep = 1;
        
        for  ( i=iLeft+nStep, bWasWrong=bWasChg=_FALSE,
              phase=_FALSE,
              iPrevGood=iLeft,
              dxprev=xArray[i]-xArray[iLeft],
              d2xprev=0;
              i<=iRight;
              i+=nStep, phase=!phase )
        {
            if  ( ( ((dytek=yArray[i]-yArray[iPrevGood]) >= dy_eps) && (dy < 0) )
                 || (   (dytek <= -dy_eps) && (dy > 0) ) )
            {
                if  ( bWasWrong )
                    return  _FALSE;
                else
                    bWasWrong = _TRUE;
            }
            else
            {
                bWasWrong = _FALSE;
                iPrevGood = i;
            }
            
            if  ( phase )
            {
                
                /*   Check, if this is not an integral or so: */
                
                if  ( (((d2xtek =   (dxtek=xArray[i]-xArray[i-TWO(nStep)])- dxprev) > dx_eps)
                         && dxtek > 1 && (d2xprev < -ONE_FOURTH(HWRAbs(dxtek)) ) )
                     || ( (d2xtek < -dx_eps) && dxtek > 1 && (d2xprev > ONE_FOURTH(HWRAbs(dxtek)) ) ) )
                {
                    if  ( bWasChg )
                        return  _FALSE;
                    else
                        bWasChg = _TRUE;
                }
                
                if  ( HWRAbs(d2xtek) > dy_eps )
                {
                    /* i.e. throw out meaningless cases*/
                    dxprev = dxtek;
                    d2xprev = d2xtek;
                }
            }
            
            /*  Check x-distance from the straight line: */
            
            dx = xArray[i] - (_SHORT)(xArray[iLeft]
                       + (((_LONG)(yArray[i]-yArray[iLeft])*lMultCoef + lDivCoef2)/ lDivCoef));
            
            TO_ABS_VALUE(dx);
            
            if  ( dx > 1 && dx >= ONE_HALF(dy) - 1 )
                return  _FALSE;
            
        } /*2*/
        
        return  _TRUE;
        
    } /*1*/
    
    return  _FALSE;
    
} /*chk_slash*/
/***********************************************/

/*   Adjust "iBeg" and "iEnd" according to the length  */
/* of the longest ~straight line within initial "iBeg" */
/* and "iEnd" points:                                  */
/*                                                     */
/*  Tips:                                              */
/*                                                     */
/*    This will be changed:                            */
/*                                                     */
/*           *ooooooooooooooooooooooooo                */
/*          oo                                         */
/*         oo                                          */
/*                                                     */
/*                                                     */
/*    This won't be changed (too big angle):           */
/*                                                     */
/*           *ooooooooooooooooooooooooo                */
/*       oooo                                          */
/*   oooo                                              */
/*                                                     */
/*                                                     */
/*    This won't be changed (too big part):            */
/*                                                     */
/*               *oooooooooooooooooooooooooooo         */
/*              o                                      */
/*             o                                       */
/*            o                                        */
/*           o                                         */
/*          o                                          */
/*         o                                           */
/*                                                     */
/*                                                     */
/*    This will be changed (sharp angle):              */
/*                                                     */
/*         *oooooooooooooooooooooooooooooooo           */
/*                ooooooo                              */
/*                       ooooooo                       */
/*                                                     */

#define  COS_WIDE_ANGLE    (-80)
#define  COS_SHARP_ANGLE   70

_VOID  FindStraightPart  ( p_SHORT xArray, p_SHORT yArray, p_INT piBeg, p_INT piEnd )
{
    _INT    iBeg        = *piBeg;
    _INT    iEnd        = *piEnd;
    _INT    dind        = ONE_THIRD(iEnd-iBeg);
    _INT    iMidBeg     = iBeg + dind;
    _INT    iMidEnd     = iEnd - dind;
    _INT    iMostFarBeg = iMostFarFromChord (xArray,yArray,iBeg,iMidBeg);
    _INT    iMostFarEnd = iMostFarFromChord (xArray,yArray,iMidEnd,iEnd);
    _INT    dindEndBeg  = ONE_THIRD(iEnd-iBeg);
    _INT    dindMostFar;
    _SHORT  cos_EndPart;
    
    dindMostFar = iMostFarBeg - iBeg;
    if  ( dindMostFar > 0  &&  iMostFarBeg != iMidBeg )  {
        cos_EndPart = (_SHORT)cos_vect(iMostFarBeg,iBeg,
                                       iMostFarBeg,iMidBeg, xArray,yArray);
        if  (   (   dindMostFar < dindEndBeg
                 && cos_EndPart > COS_WIDE_ANGLE
                 )
             || (   dindMostFar < TWO(dindEndBeg)
                 && cos_EndPart > COS_SHARP_ANGLE
                 )
             )  {
            iBeg = iMostFarBeg;
        }
    }
    
    dindMostFar = iEnd - iMostFarEnd;
    if  ( dindMostFar > 0  &&  iMostFarEnd != iMidEnd )
    {
        cos_EndPart = (_SHORT)cos_vect(iMostFarEnd,iEnd, iMostFarEnd,iMidEnd, xArray,yArray);
        if  ( ( dindMostFar < dindEndBeg && cos_EndPart > COS_WIDE_ANGLE)
             || ( dindMostFar < TWO(dindEndBeg) && cos_EndPart > COS_SHARP_ANGLE) )
        {
            iEnd = iMostFarEnd;
        }
    }
    
    *piBeg = iBeg;
    *piEnd = iEnd;
    
} /*FindStraightPart*/

#endif //#ifndef LSTRIP

