/***************************************************************************************
 *
 *  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/>.
 *
 **************************************************************************************/

#include  "dscr.h"
#include  "hwr_sys.h"

/***********  Internal Finction Prototype  *****************************/

static _WORD  TraceToOdata( p_ODATA  pOdata, p_POINT  pTrace, _WORD  nPoint , _WORD nFiltr );

static _ULONG NormCoeffs( _WORD Ord  , p_LONG pX, p_LONG pY );

static _VOID  ApprOdata( _WORD Sam  , p_ODATA    pOdata ,
						_WORD Resam, p_ARDATA   pARdata,
						_WORD Ord  , p_POINT    pCoeffs,
						_WORD nItr , p_LONG     pLam   , p_LONG  pErr );

static _WORD  TraceToOdata3D( p_ODATA3D  pOdata, p_3DPOINT  pTrace, _WORD  nPoint , _WORD nFiltr );

static _ULONG NormCoeffs3D( _WORD Ord  , p_LONG pX, p_LONG pY, p_LONG pZ );

static _VOID  ApprOdata3D( _WORD Sam  , p_ODATA3D    pOdata ,
						  _WORD Resam, p_ARDATA3D   pARdata,
						  _WORD Ord  , p_3DPOINT    pCoeffs,
						  _WORD nItr , p_LONG       pLam   , p_LONG  pErr );

/***********************************************************************/
/* _WORD TraceToOdata ( p_ODATA  pOdata,                               */
/*                      p_POINT  pTrace, _WORD  nPoint, _WORD nFiltr ) */
/* Purpose: Convert input trajectory (pTrace, nPoints)                 */
/*          into internal format and store it to Odata Array           */
/* Return : 0 on error, otherwise number of point converted and stored */
/***********************************************************************/

static _WORD TraceToOdata ( p_ODATA  pOdata, p_POINT  pTrace, _WORD   nPoint,  _WORD   nFiltr )
{
	_WORD   i, j, n, l,  m;
	_LONG   Xmin, Xmax, dx;
	_LONG   Ymin, Ymax, dy;
	p_ODATA  pTmpOd;
	
	pTmpOd = pOdata;
	for( i = n = j = 0; i < nPoint; i++, pTrace++ )
	{
		if ( pTrace->y == -1 )
		{
			if ( n )
			{
				for ( l = 0; l < nFiltr; l++ )
				{
					pTmpOd -= n;
					Xmin    = pTmpOd->x;
					Ymin    = pTmpOd->y;
					pTmpOd  ++;
					for ( m = 1; m < n; m++, pTmpOd++ )
					{
						Xmax = pTmpOd->x;
						Ymax = pTmpOd->y;
						pTmpOd->x = (Xmin + Xmax) >> 1;
						pTmpOd->y = (Ymin + Ymax) >> 1;
						Xmin = Xmax;
						Ymin = Ymax;
					}
					pTmpOd->x = Xmin;
					pTmpOd->y = Ymin;
					pTmpOd++;
					n++;
					j++;
				}
				
				if ( (pTrace+1)->y == -1 )
					break;
			}
			n = 0;
			continue;
		}
		
		pTmpOd->x = (_LONG) pTrace->x << 10;
		pTmpOd->y = (_LONG) pTrace->y << 10;
		pTmpOd++;
		j++;
		n++;
	}
	
	if ( j == 0 )
		return  0;
	
	// Find Box
	pTmpOd = pOdata;
	Xmin = Xmax = pTmpOd->x;
	Ymin = Ymax = pTmpOd->y;
	pTmpOd++;
	for ( i = 1; i < j; i++, pTmpOd++ )
	{
		dx = pTmpOd->x;
		dy = pTmpOd->y;
		if ( dx > Xmax )
			Xmax = dx;
		if ( dx < Xmin )
			Xmin = dx;
		if ( dy > Ymax )
			Ymax = dy;
		if ( dy < Ymin )
			Ymin = dy;
	}
	
	dx  =  ( Xmin + Xmax ) >> 1;
	dy  =  ( Ymin + Ymax ) >> 1;
	
	Xmax = ( Xmax - Xmin );
	Ymax = ( Ymax - Ymin );
	
	// * Fit Into Box * //
	if ( Xmax > Ymax )
		Ymax = Xmax;
	else
		Xmax = Ymax;
	
	if ( Xmax < 4096 )
		return 0;
	
	Xmax >>= 10;
	Ymax >>= 10;
	
	pTmpOd = pOdata;
	for( i = 0; i < j; i++, pTmpOd++ )
	{
		pTmpOd->x = (( pTmpOd->x - dx) << 5 ) / Xmax;
		pTmpOd->y = (( pTmpOd->y - dy) << 5 ) / Ymax;
	}
	
	pOdata->dx = 0L;
	pOdata->dy = 0L;
	pOdata->s  = 0L;
	pOdata->r  = 0L;
	pTmpOd = pOdata;
	pOdata++;
	
	for( i = 1; i < j; i++, pOdata++ )
	{
		Xmin = dx = pOdata->x - pTmpOd->x;
		Ymin = dy = pOdata->y - pTmpOd->y;
		
		if (dx == 0 && dy == 0 )
			continue;
		
		if (dx < 0 ) dx = -dx;
		if (dy < 0 ) dy = -dy;
		
		pTmpOd++;
		
		if ( dx == 0  )
		{
			pTmpOd->s = dy;
		}
		else
		{
			if ( dy == 0  )
			{
				pTmpOd->s = dx;
			}
			else
			{
				if ( dx == dy )
				{
					pTmpOd->s = ( dx * 46341L ) >> 15;
				}
				else
				{
					pTmpOd->s = SQRT32 ( (_ULONG) dx * (_ULONG) dx+ (_ULONG) dy * (_ULONG) dy );
				}
			}
		}
		
		if ( pTmpOd->s < 256L )
		{
			pTmpOd--;
			continue;
		}
		
		pTmpOd->x  = pOdata->x;
		pTmpOd->y  = pOdata->y;
		pTmpOd->dx = Xmin;
		pTmpOd->dy = Ymin;
		pTmpOd->r  = (pTmpOd-1)->r + pTmpOd->s;
	}
	
	pOdata -= j;
	j = (_WORD) ( pTmpOd - pOdata );
	
	return j + 1;
	
} // End of function TraceToOdata;



static _WORD TraceToOdata3D ( p_ODATA3D  pOdata, p_3DPOINT  pTrace, _WORD nPoint,  _WORD nFiltr )
{
	_WORD     i, j, n, l,  m;
	_LONG     Xmin, Xmax, dx;
	_LONG     Ymin, Ymax, dy;
	_LONG     Zmin, Zmax, dz;
	
	p_ODATA3D  pTmpOd;
	
	pTmpOd = pOdata;
	for( i = n = j = 0; i < nPoint; i++, pTrace++ )
	{
		if ( pTrace->y == -1 )
		{
			if ( n )
			{
				for ( l = 0; l < nFiltr; l++ )
				{
					pTmpOd -= n;
					Xmin    = pTmpOd->x;
					Ymin    = pTmpOd->y;
					Zmin    = pTmpOd->z;
					
					pTmpOd  ++;
					for ( m = 1; m < n; m++, pTmpOd++ )
					{
						Xmax = pTmpOd->x;
						Ymax = pTmpOd->y;
						Zmax = pTmpOd->z;
						
						pTmpOd->x = (Xmin + Xmax) >> 1;
						pTmpOd->y = (Ymin + Ymax) >> 1;
						pTmpOd->z = (Zmin + Zmax) >> 1;
						
						Xmin = Xmax;
						Ymin = Ymax;
						Zmin = Zmax;
					}
					
					pTmpOd->x = Xmin;
					pTmpOd->y = Ymin;
					pTmpOd->z = Zmin;
					pTmpOd++;
					n++;
					j++;
				}
				
				if ( (pTrace+1)->y == -1 )
					break;
			}
			n = 0;
			continue;
		}
		
		pTmpOd->x = (_LONG) pTrace->x << 10;
		pTmpOd->y = (_LONG) pTrace->y << 10;
		pTmpOd->z = (_LONG) pTrace->z << 10; // ??????
		
		pTmpOd++;
		j++;
		n++;
	}
	
	if ( j == 0 )
		return  0;
	
	// Find Box
	pTmpOd = pOdata;
	Xmin = Xmax = pTmpOd->x;
	Ymin = Ymax = pTmpOd->y;
	Zmin = Zmax = pTmpOd->z;
	
	pTmpOd++;
	
	for ( i = 1; i < j; i++, pTmpOd++ )
	{
		dx = pTmpOd->x;
		dy = pTmpOd->y;
		dz = pTmpOd->z;
		
		if ( dx > Xmax ) Xmax = dx;
		if ( dx < Xmin ) Xmin = dx;
		
		if ( dy > Ymax ) Ymax = dy;
		if ( dy < Ymin ) Ymin = dy;
		
		if ( dz > Zmax ) Zmax = dz;
		if ( dz < Zmin ) Zmin = dz;
	}
	
	dx  =  ( Xmin + Xmax ) >> 1;
	dy  =  ( Ymin + Ymax ) >> 1;
	dz  =  ( Zmin + Zmax ) >> 1;
	
	Xmax = ( Xmax - Xmin );
	Ymax = ( Ymax - Ymin );
	Zmax = ( Zmax - Zmin );
	
	// * Fit Into Box * //
	if ( Xmax < Ymax ) Xmax = Ymax;
	
	if ( Xmax < 4096 )
		return 0;
	
	Xmax >>= 10;
	Zmax >>= 10; if (Zmax < 1) Zmax = 1;
	
	pTmpOd = pOdata;
	for( i = 0; i < j; i++, pTmpOd++ )
	{
		pTmpOd->x = (( pTmpOd->x - dx) << 5 ) / Xmax;
		pTmpOd->y = (( pTmpOd->y - dy) << 5 ) / Xmax;
		pTmpOd->z = (( pTmpOd->z - dz) << 5 ) / Zmax;
	}
	
	pOdata->dx = 0L;
	pOdata->dy = 0L;
	pOdata->dz = 0L;
	pOdata->s  = 0L;
	pOdata->r  = 0L;
	pTmpOd = pOdata;
	pOdata++;
	
	for( i = 1; i < j; i++, pOdata++ )
	{
		Xmin = dx = pOdata->x - pTmpOd->x;
		Ymin = dy = pOdata->y - pTmpOd->y;
		Zmin = dz = pOdata->z - pTmpOd->z;
		
		if (dx == 0 && dy == 0 && dz == 0 )
			continue;
		
		if (dx < 0 )
			dx = -dx;
		if (dy < 0 )
			dy = -dy;
		if (dz < 0 )
			dz = -dz;
		
		pTmpOd++;
		
		pTmpOd->s = SQRT32 ( (_ULONG) dx * (_ULONG) dx+
							(_ULONG) dy * (_ULONG) dy+
							(_ULONG) dz * (_ULONG) dz );
		if ( pTmpOd->s < 256L )
		{
			pTmpOd--;
			continue;
		}
		
		pTmpOd->x  = pOdata->x;
		pTmpOd->y  = pOdata->y;
		pTmpOd->z  = pOdata->z;
		
		pTmpOd->dx = Xmin;
		pTmpOd->dy = Ymin;
		pTmpOd->dz = Zmin;
		
		pTmpOd->r  = (pTmpOd-1)->r + pTmpOd->s;
	}
	
	pOdata -= j;
	j = (_WORD) ( pTmpOd - pOdata );
	
	return j + 1;
	
} // End of function TraceToOdata3D;


/************************************************************************/
/* Purpose : Calculate Approximation of the curve                       */
/************************************************************************/

#define  NEXT_ADATA(Ptr)   ((p_LONG) ((p_UCHAR)Ptr+sizeof(_ARDATA)))
#define  NEXT_ADATA3D(Ptr) ((p_LONG) ((p_UCHAR)Ptr+sizeof(_ARDATA3D)))

#define     MAX_ORDER     16
#define     MAX_RESAM     32

static _VOID  ApprOdata ( _WORD Sam  ,  p_ODATA   pOdata ,
				  _WORD Resam,  p_ARDATA  pARdata,
				  _WORD Ord  ,  p_POINT   pCoeffs,
				  _WORD nItr ,  p_LONG    pLam   , p_LONG  pErr )
{
	_WORD   i, j;
	_WORD   k,Sh=0;
	_LONG    Lam;
	_LONG    Err=0;
	p_LONG   pAR = NULL;
	p_LONG   pD = NULL;
    _LONG   CfsX[MAX_ORDER] = {0};
    _LONG   CfsY[MAX_ORDER] = {0};
    _LONG   TrfBuf[MAX_RESAM] = {0};
	
	if ( Resam == 16 )
		Sh = 3;
	if ( Resam == 32 )
		Sh = 4;
	
	ResetParam ( Resam, pARdata, pOdata[Sam-1].r );
	for ( i = 0; i < nItr; i++ )
	{
		Lam = Repar( Sam, pOdata, Resam, pARdata );
		
		for ( k = 0; k < 2; k++ ) // Dim
		{
			pD = TrfBuf;
			if ( k == 0 )
				pAR  =  &pARdata->Rx;
			if ( k == 1 )
				pAR  =  &pARdata->Ry;
			
			for( j = 0; j < Resam; j++,pD++,pAR = NEXT_ADATA(pAR))
				*pD = *pAR;
			
			// Forward Transform
			if ( Resam == 16 )
				FDCT16 ( TrfBuf );
			if ( Resam == 32 )
				FDCT32 ( TrfBuf );
			
			// Cut Coefficient
			pD  = TrfBuf;
			*pD >>= Sh+1;
			pD ++;
			for( j = 1  ; j < Ord  ; j++, pD ++)
				*pD >>= Sh;
			for( j = Ord; j < Resam; j++, pD ++)
				*pD   =  0;
			
			if ( i == nItr-1 )
			{ // Save Coefficient
				pD     = TrfBuf;
				if ( k == 0 )
					pAR = CfsX;
				if ( k == 1 )
					pAR = CfsY;
				for( j = 0; j < Ord; j++)
					*pAR++ = *pD++;
			}
			
			// Inverce Transform
			if ( Resam == 16 )
				IDCT16 ( TrfBuf );
			if ( Resam == 32 )
				IDCT32 ( TrfBuf );
			
			pD     = TrfBuf;
			if ( k == 0 )
				pAR  =  &pARdata->Ax;
			if ( k == 1 )
				pAR  =  &pARdata->Ay;
			for( j = 0; j < Resam; j++,pD++,pAR = NEXT_ADATA(pAR))
				*pAR = *pD;
		}  // End of k;
		
		
		if (  pErr && i == nItr-1 )
			Err = ApprError ( (_INT) Resam,  pARdata );
		
		// Stabilization
		for ( j = 0; j < Resam; j++, pARdata++ )
		{
			pARdata->Ax = (pARdata->Ax + pARdata->Rx) >> 1;
			pARdata->Ay = (pARdata->Ay + pARdata->Ry) >> 1;
		}
		
		pARdata -= Resam;
		
		Tracing( Resam, pARdata );
	} // End of i
	
	// MAR Temporaly
	Lam = Repar( Sam, pOdata, Resam, pARdata );
	// MAR
	
	NormCoeffs ( Ord, CfsX, CfsY );
	
	// Return Coefficient
	for ( i = 0; i < Ord; i++, pCoeffs++ )
	{
		pCoeffs->x = (_SHORT) (CfsX[i] >> 8);
		pCoeffs->y = (_SHORT) (CfsY[i] >> 8);
	}
	
	if ( pLam  )
		*pLam = Lam;
	if ( pErr  )
		*pErr = Err;
} // End of ApprOdata


static _VOID  ApprOdata3D ( _WORD Sam  ,  p_ODATA3D   pOdata ,
					_WORD Resam,  p_ARDATA3D  pARdata,
					_WORD Ord  ,  p_3DPOINT   pCoeffs,
					_WORD nItr ,  p_LONG      pLam   , p_LONG  pErr )
{
	_WORD   i, j;
	_WORD   k,Sh = 2;
	_LONG    Lam;
	_LONG    Err=0;
	p_LONG   pAR = NULL;
	p_LONG   pD = NULL;
    _LONG   CfsX  [MAX_ORDER] = {0};
    _LONG   CfsY  [MAX_ORDER] = {0};
    _LONG   CfsZ  [MAX_ORDER] = {0};
    _LONG   TrfBuf[MAX_RESAM] = {0};
	
	if ( Resam == 16 )  Sh = 3;
	if ( Resam == 32 )  Sh = 4;
	
	ResetParam3D ( Resam, pARdata, pOdata[Sam-1].r );
	for ( i = 0; i < nItr; i++ )
	{
		Lam = Repar3D ( Sam, pOdata, Resam, pARdata );
		
		for ( k = 0; k < 3; k++ ) // Dim
		{
			pD   =  TrfBuf;
			if ( k == 0 )
				pAR  =  &pARdata->Rx;
			if ( k == 1 )
				pAR  =  &pARdata->Ry;
			if ( k == 2 )
				pAR  =  &pARdata->Rz;
			
			for( j = 0; j < Resam; j++,pD++,pAR = NEXT_ADATA3D(pAR))
				*pD = *pAR;
			
			// Forward Transform
			if ( Resam == 16 )
				FDCT16 ( TrfBuf );
			if ( Resam == 32 )
				FDCT32 ( TrfBuf );
			
			// Cut Coefficient
			pD  = TrfBuf;
			*pD >>= Sh+1;
			pD ++;
			for( j = 1  ; j < Ord  ; j++, pD ++)
				*pD >>= Sh;
			for( j = Ord; j < Resam; j++, pD ++)
				*pD   =  0;
			
			if ( i == nItr-1 )
			{
				// Save Coefficient
				pD     = TrfBuf;
				if ( k == 0 )
					pAR = CfsX;
				if ( k == 1 )
					pAR = CfsY;
				if ( k == 2 )
					pAR = CfsZ;
				for( j = 0; j < Ord; j++)
					*pAR++ = * pD++;
			}
			
			//TempTest
			
			// Inverce Transform
			//         if ( Resam == 16 ) IDCT16 ( TrfBuf );
			//         if ( Resam == 32 ) IDCT32 ( TrfBuf );
			
			//         pD     = TrfBuf;
			//         if ( k == 0 ) pAR  =  &pARdata->Ax;
			//         if ( k == 1 ) pAR  =  &pARdata->Ay;
			//         if ( k == 2 ) pAR  =  &pARdata->Az;
			//         for( j = 0; j < Resam; j++,pD++,pAR = NEXT_ADATA3D(pAR)) *pAR = *pD;
		}  // End of k;
		
		//      if (  pErr && i == nItr-1 )
		//         Err = ApprError3D ( (_INT) Resam,  pARdata );
		
		// Stabilization
		//      for ( j = 0; j < Resam; j++, pARdata++ )
		//       {
		//          pARdata->Ax = (pARdata->Ax + pARdata->Rx) >> 1;
		//          pARdata->Ay = (pARdata->Ay + pARdata->Ry) >> 1;
		//          pARdata->Az = (pARdata->Az + pARdata->Rz) >> 1;
		//       }
		//
		//      pARdata -= Resam;
		//
		//      Tracing3D ( Resam, pARdata );
	} // End of i
	
	// MAR Temporaly
	//    Lam = Repar3D ( Sam, pOdata, Resam, pARdata );
	Lam = 0;
	// MAR
	
	NormCoeffs3D ( Ord, CfsX, CfsY, CfsZ ); // MAR ????
	
	// Return Coefficient
	for ( i = 0; i < Ord; i++, pCoeffs++ )
	{
		pCoeffs->x = (_SHORT) (CfsX[i] >> 8);
		pCoeffs->y = (_SHORT) (CfsY[i] >> 8);
		pCoeffs->z = (_SHORT) (CfsZ[i] >> 8); // MAR ????
	}
	
	if ( pLam  )
		*pLam = Lam;
	if ( pErr  )
		*pErr = Err;
} // End of ApprOdata3D


/*******************************************************************/
/* Purpose: Normalize the set of coefficients                      */
/*******************************************************************/

static _ULONG  NormCoeffs ( _WORD Ord, p_LONG pX, p_LONG pY )
{
	_WORD   i;
	_LONG   X = 1, Y = 1;
	_ULONG  S = 0L;
	
	pX++; pY++;
	for ( i = 1; i < Ord; i++, pX++, pY++ )
	{
		X = (*pX);
		Y = (*pY);
		S += X*X + Y*Y;
	}
	
	pX -= Ord;
	pY -= Ord;
	
	S = SQRT32 (S) >> 5;
	
	for ( i = 0; i < Ord; i++, pX++, pY++ )
	{
		*pX =  (*pX << 10) / (_LONG) S;
		*pY =  (*pY << 10) / (_LONG) S;
	}
	
	return  S;
}

static _ULONG  NormCoeffs3D ( _WORD Ord, p_LONG pX, p_LONG pY, p_LONG pZ )
{
	_WORD   i;
	_LONG   X = 1, Y = 1, Z = 1;
	_ULONG  S = 0L;
	
	pX++; pY++; pZ++;
	for ( i = 1; i < Ord; i++, pX++, pY++, pZ++ )
	{
		X = *pX;
		Y = *pY;
		Z = *pZ;
		S += X*X + Y*Y + Z*Z;
	}
	
	pX -= Ord;
	pY -= Ord;
	pZ -= Ord;
	
	S = SQRT32 (S) >> 5;
	
	for ( i = 0; i < Ord; i++, pX++, pY++, pZ++ )
	{
		*pX =  (*pX << 10) / (_LONG) S;
		*pY =  (*pY << 10) / (_LONG) S;
		*pZ =  (*pZ << 10) / (_LONG) S;
	}
	return  S;
}

/*****************************************************************************/
/*  MarkTails: Mark small tails to be deleted by CutTails                    */
/*****************************************************************************/

_BOOL MarkTails ( _WORD m_nPnt, p_POINT m_pPnt, p_POINT m_pThk )
{
	_INT    i,j;
	_WORD   idx1 ;
	_WORD   idx2 ;
	_WORD   nPnt ;
	p_POINT  pPnt ;
	p_POINT  pThk ;
	p_POINT  pCurr;
	p_POINT  pPrev;
	p_POINT  pNext;
	
	if ( m_nPnt   < 8  )
		return _FALSE;
	
	if ( m_pPnt == _NULL || m_pThk == _NULL )
		return _FALSE;
	
	nPnt = m_nPnt - 2; // Skip First Break
	pPnt = m_pPnt + 1; // Skip First Break
	pThk = m_pThk + 1; // Skip First Break
	
	pCurr =  pPnt ;
	for ( i = 0; i < (_INT) nPnt; i++ )
	{
		j = 0;
		do {
			j++;
			idx1  = (nPnt + (i-j)) % nPnt;
			idx2  = (nPnt + (i+j)) % nPnt;
			pPrev = pCurr + idx1;
			pNext = pCurr + idx2;
		} while ( pPrev->x == pNext->x && pPrev->y == pNext->y && j <= 4 );
		
		if ( j == 1 )
			continue;
		
		if ( j >  4 )
			continue;
		
		while ( --j >= 0 )
		{
			idx1  = (nPnt + (i-j)) % nPnt;
			idx2  = (nPnt + (i+j)) % nPnt;
			pThk[idx1].y  =  1;
			pThk[idx2].y  =  1;
		}
	}
	
	return _TRUE;
} // End of MarkTails ();

_WORD CutTails ( _WORD m_nPnt, p_POINT m_pPnt, p_POINT m_pThk )
{
	_WORD    i,j;
	p_POINT  pNewTrc = m_pPnt;
	p_POINT  pNewThk = m_pThk;
	p_POINT  pOldTrc = m_pPnt;
	p_POINT  pOldThk = m_pThk;
	
	for ( i = j = 0; i < m_nPnt + 1; i++, pOldTrc++, pOldThk++ )
	{
		// For All Points
		if ( m_pThk[i].y )
			continue;
		
		if ( pOldTrc->y != -1 )
		{
			if ( (pNewTrc-1)->x == pOldTrc->x &&  (pNewTrc-1)->y == pOldTrc->y  )
				continue;
		}
		
		pNewTrc->x = pOldTrc->x;
		pNewTrc->y = pOldTrc->y;
		pNewThk->x = pOldThk->x;
		pNewThk->y = pOldThk->y;
		pNewTrc++;
		pNewThk++;
		j++;
	}
	
	m_nPnt  = j  -  1;
	
	if ( m_pPnt[1].x == m_pPnt[m_nPnt - 2].x && m_pPnt[1].y == m_pPnt[m_nPnt - 2].y  )
	{
		pNewTrc = m_pPnt + m_nPnt - 2;
		pNewThk = m_pThk + m_nPnt - 2;
		
		pNewTrc->x =  0;
		pNewTrc->y = -1;
		pNewThk->x =  0;
		pNewThk->y =  0;
		
		pNewTrc++;
		pNewThk++;
		
		pNewTrc->x =  0;
		pNewTrc->y = -1;
		pNewThk->x =  0;
		pNewThk->y =  0;
		
		m_nPnt--;
	}
	
	return m_nPnt;
} // End of CutTails

/****************************************************************/
/* Purpose : Convert trace to dct representation                */
/****************************************************************/

_BOOL _FPREFIX Trace2DToDct ( _WORD nTrace, p_POINT pTrace   ,
							 _WORD  Order, p_POINT pCoeffs  ,
							 _WORD  nItr ,  _WORD  nFiltrItr,
							 p_LONG  pLam , p_LONG  pErr     ,
							 _BOOL  fCutTails )
{
	_WORD    Sam;
	_WORD    Resam;
	_BOOL    fRet    = _TRUE;
	p_POINT   pThk    = _NULL;
	p_POINT   pPnt    = _NULL;
	p_ODATA   pOdata  = _NULL;
	p_ARDATA  pARdata = _NULL;
	
	if ( Order > 16 || Order < 4 )
		return _FALSE;
	
#if 0
	_WORD     nPnt;
	/* Duplicate Trace */
	pPnt =  (p_POINT) HWRMemoryAlloc ((nTrace+3) * sizeof(_POINT));
	if ( pPnt == _NULL )
	{
		fRet = _FALSE;
		goto Exit;
	}
	
	pThk =  (p_POINT) HWRMemoryAlloc ((nTrace+3) * sizeof(_POINT));
	if ( pThk == _NULL )
	{
		fRet = _FALSE;
		goto Exit;
	}
	
	HWRMemSet ( pThk, 0, (nTrace + 3) * sizeof(_POINT));
	
	if ( pTrace->y != -1 )
	{
		pPnt->x =  0;
		pPnt->y = -1;
		HWRMemCpy ( pPnt+1, pTrace, nTrace * sizeof(_POINT));
		pPnt[nTrace+1].x =  0;
		pPnt[nTrace+1].y = -1;
		pPnt[nTrace+2].x =  0;
		pPnt[nTrace+2].y = -1;
	}
	else
	{
		HWRMemCpy ( pPnt  , pTrace, nTrace * sizeof(_POINT));
		pPnt[nTrace  ].x =  0;
		pPnt[nTrace  ].y = -1;
		pPnt[nTrace+1].x =  0;
		pPnt[nTrace+1].y = -1;
	}
	
	nPnt = 1;
	while ( pPnt[nPnt-1].y != -1 || pPnt[nPnt].y != -1 ) nPnt++;
	
	if ( fCutTails )
	{
		if ( MarkTails ( nPnt, pPnt, pThk ))
			nPnt = CutTails (nPnt, pPnt, pThk );
	}
	
	nTrace = nPnt;
	pTrace = pPnt;
	
	Sam = 1;
	for ( Resam = 0; Resam < nTrace; Resam++, pTrace++ )
	{
		if ( pTrace->y == -1 )
			Sam ++;
	}
	
	pTrace -= nTrace;
	
	if ( Order > 7 )
		Resam = 32;
	else
		Resam = 16;
	
	pOdata  = (p_ODATA) HWRMemoryAlloc ((nTrace + Sam * nFiltrItr) * sizeof(_ODATA) );
	if ( pOdata == _NULL )
	{
		fRet = _FALSE;
		goto Exit;
	}
	
	pARdata = (p_ARDATA) HWRMemoryAlloc ((Resam  + 1) * sizeof(_ARDATA));
	if ( pARdata == _NULL )
	{
		fRet = _FALSE;
		goto Exit;
	}
	
#else
	Resam = 32;
	
	
	pOdata  = (p_ODATA) HWRMemoryAlloc (nTrace*sizeof(_ODATA) + (Resam+1)*sizeof(_ARDATA));
	if ( pOdata == _NULL )
	{
		fRet = _FALSE;
		goto Exit;
	}
	
	pARdata = (p_ARDATA)((p_UCHAR)pOdata + nTrace*sizeof(_ODATA));
#endif
	
	Sam = TraceToOdata( pOdata, pTrace, nTrace, nFiltrItr );
	if ( Sam < 2 )
	{
		fRet = _FALSE;
		goto Exit;
	}
	
	ApprOdata ( Sam, pOdata, Resam, pARdata, Order, pCoeffs, nItr, pLam, pErr );
	
Exit:
	
	if ( pThk )
		HWRMemoryFree ( pThk );
	if ( pPnt )
		HWRMemoryFree ( pPnt );
	if ( pOdata )
		HWRMemoryFree ( pOdata );
	return  fRet;
}

_BOOL _FPREFIX Trace3DToDct ( _WORD nTrace, p_3DPOINT pTrace ,
							 _WORD  Order, p_3DPOINT pCoeffs,
							 _WORD  nItr ,  _WORD  nFiltrItr,
							 p_LONG  pLam , p_LONG  pErr     ,
							 _BOOL  fCutTails )
{
	_WORD      Sam;
	_WORD      Resam;
	_BOOL      fRet    = _TRUE;
	p_3DPOINT   pThk    = _NULL;
	p_3DPOINT   pPnt    = _NULL;
	p_ODATA3D   pOdata  = _NULL;
	p_ARDATA3D  pARdata = _NULL;
	
	if ( Order > 16 || Order < 4 )
		return _FALSE;
	
	Resam = 32;
	
	pOdata  = (p_ODATA3D) HWRMemoryAlloc (nTrace*sizeof(_ODATA3D) + (Resam+1)*sizeof(_ARDATA3D));
	if ( pOdata == _NULL )
	{
		fRet = _FALSE;
		goto Exit;
	}
	
	pARdata = (p_ARDATA3D)((p_UCHAR)pOdata + nTrace*sizeof(_ODATA3D));
	
	Sam = TraceToOdata3D ( pOdata, pTrace, nTrace, nFiltrItr );
	
	if ( Sam < 2 )
	{
		fRet = _FALSE;
		goto Exit;
	}
	
	// ttTime  = GetTickCount();
	
	ApprOdata3D( Sam, pOdata, Resam, pARdata, Order, pCoeffs, nItr, pLam, pErr );
	
Exit:
	
	if ( pThk )
		HWRMemoryFree ( pThk );
	if ( pPnt )
		HWRMemoryFree ( pPnt );
	if ( pOdata )
		HWRMemoryFree ( pOdata );
	
	return  fRet;
}


/*******************************************************************/
/* Purpose : Restore curve from dct coefficients                   */
/*******************************************************************/

_BOOL _FPREFIX DctToCurve2D ( _WORD Order, p_POINT pCfs , _WORD Resam, p_POINT pCrv )
{
	_WORD    i;
	_LONG    TrfBuf[MAX_RESAM];
	
	if ( !(Resam == 16 || Resam == 32))
		return _FALSE;
	
	// For X
	for ( i = 0; i < Order; i++ )
		TrfBuf[i] = (_LONG) (pCfs[i].x << 8);
	for ( i = Order; i < Resam; i++ )
		TrfBuf[i] = 0;
	if ( Resam == 16 )
		IDCT16 ( TrfBuf );
	if ( Resam == 32 )
		IDCT32 ( TrfBuf );
	for ( i = 0; i < Resam; i++ )
		pCrv[i].x =(_SHORT) ( TrfBuf[i] >> 8 );
	
	// For Y
	for ( i = 0; i < Order; i++ )
		TrfBuf[i] = (_LONG) (pCfs[i].y << 8);
	for ( i = Order; i < Resam; i++ ) TrfBuf[i] = 0;
	if ( Resam == 16 )
		IDCT16 ( TrfBuf );
	if ( Resam == 32 )
		IDCT32 ( TrfBuf );
	for ( i = 0; i < Resam; i++ )
		pCrv[i].y =(_SHORT) ( TrfBuf[i] >> 8 );
	
	return _TRUE;
}


_BOOL _FPREFIX DctToCurve3D ( _WORD Order, p_3DPOINT pCfs , _WORD Resam, p_3DPOINT pCrv )
{
	_WORD    i;
	_LONG    TrfBuf[MAX_RESAM];
	
	if ( !(Resam == 16 || Resam == 32))
		return _FALSE;
	
	// For X
	for ( i = 0;     i < Order; i++ )
		TrfBuf[i] = (_LONG) (pCfs[i].x << 8);
	for ( i = Order; i < Resam; i++ )
		TrfBuf[i] = 0;
	if ( Resam == 16 )
		IDCT16 ( TrfBuf );
	if ( Resam == 32 )
		IDCT32 ( TrfBuf );
	for ( i = 0; i < Resam; i++ )
		pCrv[i].x =(_SHORT) ( TrfBuf[i] >> 8 );
	
	// For Y
	for ( i = 0; i < Order; i++ )
		TrfBuf[i] = (_LONG) (pCfs[i].y << 8);
	for ( i = Order; i < Resam; i++ )
		TrfBuf[i] = 0;
	if ( Resam == 16 )
		IDCT16 ( TrfBuf );
	if ( Resam == 32 )
		IDCT32 ( TrfBuf );
	for ( i = 0; i < Resam; i++ )
		pCrv[i].y =(_SHORT) ( TrfBuf[i] >> 8 );
	
	// For Z
	for ( i = 0; i < Order; i++ )
		TrfBuf[i] = (_LONG) (pCfs[i].z << 8); // ??????
	for ( i = Order; i < Resam; i++ )
		TrfBuf[i] = 0;
	if ( Resam == 16 )
		IDCT16 ( TrfBuf );
	if ( Resam == 32 )
		IDCT32 ( TrfBuf );
	for ( i = 0; i < Resam; i++ )
		pCrv[i].z =(_SHORT) ( TrfBuf[i] >> 8 );
	
	return _TRUE;
}

