Files
harbour-core/src/vm/asort.c
vszakats 9687850865 2013-03-16 02:10 UTC+0100 Viktor Szakats (harbour syenar.net)
* (all files)
    * stripped svn header
    * minor cleanups
    ; use following command to find out the history of files:
       git log
       git log --follow
       git blame
       git annotate
2013-03-16 02:11:42 +01:00

260 lines
8.3 KiB
C

/*
* Harbour Project source code:
* ASort() function
*
* Copyright 1999-2001 Viktor Szakats (harbour syenar.net)
* Jose Lalin <dezac@corevia.com>
* www - http://harbour-project.org
*
* This program 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 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software; see the file COPYING.txt. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307 USA (or visit the web site http://www.gnu.org/).
*
* As a special exception, the Harbour Project gives permission for
* additional uses of the text contained in its release of Harbour.
*
* The exception is that, if you link the Harbour libraries with other
* files to produce an executable, this does not by itself cause the
* resulting executable to be covered by the GNU General Public License.
* Your use of that executable is in no way restricted on account of
* linking the Harbour library code into it.
*
* This exception does not however invalidate any other reasons why
* the executable file might be covered by the GNU General Public License.
*
* This exception applies only to the code released by the Harbour
* Project under the name Harbour. If you copy code from other
* Harbour Project or Free Software Foundation releases into a copy of
* Harbour, as the General Public License permits, the exception does
* not apply to the code that you add in this way. To avoid misleading
* anyone as to the status of such modified files, you must delete
* this exception notice from them.
*
* If you write modifications of your own for Harbour, it is your choice
* whether to permit this exception to apply to your modifications.
* If you do not wish that, delete this exception notice.
*
*/
/* TOFIX: The sorting engine requires signed indexes to work, this means
that arrays larger than 2^31 elements cannot be sorted. [vszakats] */
/* NOTE: Based on PD code found in
SORTING AND SEARCHING ALGORITHMS: A COOKBOOK, BY THOMAS NIEMANN
http://members.xoom.com/_XMCM/thomasn/s_man.htm */
#include "hbvmint.h"
#include "hbapiitm.h"
#include "hbvm.h"
#include "hbstack.h"
static HB_BOOL hb_itemIsLess( PHB_ITEM pItem1, PHB_ITEM pItem2, PHB_ITEM pBlock, PHB_BASEARRAY pBaseArray, HB_SIZE nLast )
{
if( pBlock )
{
hb_vmPushEvalSym();
hb_vmPush( pBlock );
hb_vmPush( pItem1 );
hb_vmPush( pItem2 );
hb_vmSend( 2 );
if( pBaseArray->nLen <= nLast )
return HB_FALSE;
else
{
PHB_ITEM pRet = hb_param( -1, HB_IT_ANY );
/* CA-Cl*pper always takes return value as logical item
* accepting 0, 1 as numeric representation of HB_FALSE/HB_TRUE
*/
if( HB_IS_LOGICAL( pRet ) ||
HB_IS_NUMERIC( pRet ) )
return hb_itemGetL( pRet );
else
return HB_TRUE;
}
}
/* Do native compare when no codeblock is supplied */
if( HB_IS_STRING( pItem1 ) && HB_IS_STRING( pItem2 ) )
return hb_itemStrCmp( pItem1, pItem2, HB_FALSE ) < 0;
else if( HB_IS_NUMINT( pItem1 ) && HB_IS_NUMINT( pItem2 ) )
/* intentionally separate comparison for integer numbers
to avoid precision lose in 64bit integer to double conversion */
return hb_itemGetNInt( pItem1 ) < hb_itemGetNInt( pItem2 );
else if( HB_IS_NUMERIC( pItem1 ) && HB_IS_NUMERIC( pItem2 ) )
return hb_itemGetND( pItem1 ) < hb_itemGetND( pItem2 );
else if( HB_IS_DATETIME( pItem1 ) && HB_IS_DATETIME( pItem2 ) )
/* it's not exact comparison, compare only julian date */
return hb_itemGetDL( pItem1 ) < hb_itemGetDL( pItem2 );
else if( HB_IS_LOGICAL( pItem1 ) && HB_IS_LOGICAL( pItem2 ) )
return hb_itemGetL( pItem1 ) < hb_itemGetL( pItem2 );
else
{
/* NOTE: For non-matching types CA-Cl*pper sorts always like this:
Array/Object Block String Logical Date Numeric NIL [jlalin] */
int iWeight1;
int iWeight2;
if( HB_IS_ARRAY( pItem1 ) ) iWeight1 = 1;
else if( HB_IS_BLOCK( pItem1 ) ) iWeight1 = 2;
else if( HB_IS_STRING( pItem1 ) ) iWeight1 = 3;
else if( HB_IS_LOGICAL( pItem1 ) ) iWeight1 = 4;
else if( HB_IS_DATETIME( pItem1 ) ) iWeight1 = 5;
else if( HB_IS_NUMERIC( pItem1 ) ) iWeight1 = 6;
else iWeight1 = 7;
if( HB_IS_ARRAY( pItem2 ) ) iWeight2 = 1;
else if( HB_IS_BLOCK( pItem2 ) ) iWeight2 = 2;
else if( HB_IS_STRING( pItem2 ) ) iWeight2 = 3;
else if( HB_IS_LOGICAL( pItem2 ) ) iWeight2 = 4;
else if( HB_IS_DATETIME( pItem2 ) ) iWeight2 = 5;
else if( HB_IS_NUMERIC( pItem2 ) ) iWeight2 = 6;
else iWeight2 = 7;
return iWeight1 < iWeight2;
}
}
/* partition array pItems[lb..ub] */
static HB_ISIZ hb_arraySortQuickPartition( PHB_BASEARRAY pBaseArray, HB_ISIZ lb, HB_ISIZ ub, PHB_ITEM pBlock )
{
HB_ISIZ i, j;
/* select pivot and exchange with 1st element */
i = lb + ( ( ub - lb ) >> 1 );
if( i != lb )
hb_itemSwap( pBaseArray->pItems + lb, pBaseArray->pItems + i );
/* sort lb+1..ub based on pivot */
i = lb + 1;
j = ub;
for( ;; )
{
while( j >= i && ! hb_itemIsLess( pBaseArray->pItems + j, pBaseArray->pItems + lb, pBlock, pBaseArray, j ) )
j--;
while( i < j && ! hb_itemIsLess( pBaseArray->pItems + lb, pBaseArray->pItems + i, pBlock, pBaseArray, i ) )
i++;
if( i >= j )
break;
/* Swap the items */
hb_itemSwap( pBaseArray->pItems + i, pBaseArray->pItems + j );
j--;
i++;
}
/* pivot belongs in pBaseArray->pItems[ j ] */
if( j > lb && pBaseArray->nLen > ( HB_SIZE ) j )
hb_itemSwap( pBaseArray->pItems + lb, pBaseArray->pItems + j );
return j;
}
/* sort array pBaseArray->pItems[lb..ub] */
static void hb_arraySortQuick( PHB_BASEARRAY pBaseArray, HB_ISIZ lb, HB_ISIZ ub, PHB_ITEM pBlock )
{
HB_ISIZ m;
while( lb < ub )
{
if( ( HB_SIZE ) ub >= pBaseArray->nLen )
{
ub = pBaseArray->nLen - 1;
if( lb >= ub )
break;
}
/* partition into two segments */
m = hb_arraySortQuickPartition( pBaseArray, lb, ub, pBlock );
/* sort the smallest partition to minimize stack requirements */
if( m - lb <= ub - m )
{
hb_arraySortQuick( pBaseArray, lb, m - 1, pBlock );
lb = m + 1;
}
else
{
hb_arraySortQuick( pBaseArray, m + 1, ub, pBlock );
ub = m - 1;
}
}
}
HB_BOOL hb_arraySort( PHB_ITEM pArray, HB_SIZE * pnStart, HB_SIZE * pnCount, PHB_ITEM pBlock )
{
HB_TRACE( HB_TR_DEBUG, ( "hb_arraySort(%p, %p, %p, %p)", pArray, pnStart, pnCount, pBlock ) );
if( HB_IS_ARRAY( pArray ) )
{
PHB_BASEARRAY pBaseArray = pArray->item.asArray.value;
HB_SIZE nLen = pBaseArray->nLen;
HB_SIZE nStart;
HB_SIZE nCount;
HB_SIZE nEnd;
if( pnStart && *pnStart >= 1 )
nStart = *pnStart;
else
nStart = 1;
if( nStart <= nLen )
{
if( pnCount && *pnCount >= 1 && ( *pnCount <= nLen - nStart ) )
nCount = *pnCount;
else
nCount = nLen - nStart + 1;
if( nStart + nCount > nLen ) /* check range */
nCount = nLen - nStart + 1;
nEnd = nCount + nStart - 2;
/* Optimize when only one or no element is to be sorted */
if( nCount > 1 )
hb_arraySortQuick( pBaseArray, nStart - 1, nEnd, pBlock );
}
return HB_TRUE;
}
else
return HB_FALSE;
}
HB_FUNC( ASORT )
{
PHB_ITEM pArray = hb_param( 1, HB_IT_ARRAY );
if( pArray && ! hb_arrayIsObject( pArray ) )
{
HB_SIZE nStart = hb_parns( 2 );
HB_SIZE nCount = hb_parns( 3 );
hb_arraySort( pArray,
HB_ISNUM( 2 ) ? &nStart : NULL,
HB_ISNUM( 3 ) ? &nCount : NULL,
hb_param( 4, HB_IT_BLOCK ) );
hb_itemReturn( pArray ); /* ASort() returns the array itself */
}
}