mirror of
https://github.com/mcneel/opennurbs.git
synced 2026-03-04 14:09:41 +08:00
334 lines
8.6 KiB
C++
334 lines
8.6 KiB
C++
/* $NoKeywords: $ */
|
|
/*
|
|
//
|
|
// Copyright (c) 1993-2011 Robert McNeel & Associates. All rights reserved.
|
|
// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert
|
|
// McNeel & Assoicates.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
|
|
// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF
|
|
// MERCHANTABILITY ARE HEREBY DISCLAIMED.
|
|
//
|
|
// For complete openNURBS copyright information see <http://www.opennurbs.org>.
|
|
//
|
|
////////////////////////////////////////////////////////////////
|
|
*/
|
|
|
|
/*
|
|
See opennurbs_sort.cpp for examples showing how to use
|
|
opennurbs_quacksort_template.h to define type specific
|
|
sorting functions.
|
|
*/
|
|
|
|
#if !defined(ON_QSORT_FNAME)
|
|
#error Define ON_QSORT_FNAME macro before including opennurbs_quacksort_template.h
|
|
#endif
|
|
|
|
// ON_SORT_TEMPLATE_TYPE -> double, int, ....
|
|
#if !defined(ON_SORT_TEMPLATE_TYPE)
|
|
|
|
#define BASETYPE void *
|
|
#define DATATYPE unsigned char
|
|
#define DATAWIDTH m_width
|
|
|
|
#define Swap(a,b) m_swapfunc(a,b,m_width)
|
|
|
|
#if defined(ON_SORT_TEMPLATE_USE_CONTEXT)
|
|
// use a compare function with context parameter
|
|
#define GreaterThan(A,B) m_compare(m_context,A,B) > 0
|
|
#else
|
|
// use a compare function without context parameter
|
|
#define GreaterThan(A,B) m_compare(A,B) > 0
|
|
#endif
|
|
|
|
#else
|
|
|
|
#define BASETYPE ON_SORT_TEMPLATE_TYPE *
|
|
#define DATATYPE ON_SORT_TEMPLATE_TYPE
|
|
#define DATAWIDTH 1
|
|
|
|
#if defined(ON_SORT_TEMPLATE_USE_SWAP)
|
|
#define Swap(a,b) m_swapfunc(a,b,m_width)
|
|
#else
|
|
// use intrinsic assigment
|
|
#define Swap(a,b) ON_SORT_TEMPLATE_TYPE tmp = *a; *a = *b; *b = tmp
|
|
#endif
|
|
|
|
#if defined(ON_SORT_TEMPLATE_COMPARE)
|
|
// use a compare function like strcmp for char* strings
|
|
#define GreaterThan(A,B) ON_SORT_TEMPLATE_COMPARE(A,B) > 0
|
|
#else
|
|
// use intrinsic type compares
|
|
#define GreaterThan(A,B) *A > *B
|
|
#endif
|
|
|
|
#endif
|
|
|
|
#if !defined(ON_QUACKSORT_SWAP_FUNCS_DEFINED)
|
|
#if !defined(ON_SORT_TEMPLATE_TYPE) || defined(ON_SORT_TEMPLATE_USE_SWAP)
|
|
|
|
// In some files this template is used multiple times.
|
|
// The ON_QUACKSORT_SWAP_FUNCS_DEFINED define prevents
|
|
// multiple definitions of the static Swap*() functions.
|
|
#define ON_QUACKSORT_SWAP_FUNCS_DEFINED
|
|
|
|
static void SwapChars( unsigned char* a, unsigned char* b, size_t width)
|
|
{
|
|
do
|
|
{
|
|
unsigned char x = *a;
|
|
*a++ = *b;
|
|
*b++ = x;
|
|
}
|
|
while( --width);
|
|
}
|
|
|
|
static void SwapInts( unsigned char* a, unsigned char* b, size_t width)
|
|
{
|
|
ON__UINT32* ai = (ON__UINT32*)a;
|
|
ON__UINT32* bi = (ON__UINT32*)b;
|
|
do
|
|
{
|
|
ON__UINT32 x = *ai;
|
|
*ai++ = *bi;
|
|
*bi++ = x;
|
|
width -= sizeof(x);
|
|
}
|
|
while( width);
|
|
}
|
|
|
|
static void SwapBigInts( unsigned char* a, unsigned char* b, size_t width)
|
|
{
|
|
ON__UINT64* ai = (ON__UINT64*)a;
|
|
ON__UINT64* bi = (ON__UINT64*)b;
|
|
do
|
|
{
|
|
ON__UINT64 x = *ai;
|
|
*ai++ = *bi;
|
|
*bi++ = x;
|
|
width -= sizeof(x);
|
|
}
|
|
while( width);
|
|
}
|
|
|
|
#endif
|
|
#endif
|
|
|
|
// implementation of quick sort with minimum swaps for partition sizes 4 and less
|
|
void ON_quacksort(
|
|
BASETYPE *base
|
|
,size_t nel
|
|
#if !defined(ON_SORT_TEMPLATE_TYPE)
|
|
,size_t width
|
|
#if defined(ON_SORT_TEMPLATE_USE_CONTEXT)
|
|
,int (*compar)(void*, const void *, const void *)
|
|
,void* context
|
|
#else
|
|
,int (*compar)(const void *, const void *)
|
|
#endif
|
|
#endif
|
|
)
|
|
{
|
|
class CSorter
|
|
{
|
|
public:
|
|
DATATYPE *m_base;
|
|
size_t m_nel;
|
|
const size_t m_width;
|
|
int (*m_compar)(const void *, const void *);
|
|
void (*m_swapfunc)(unsigned char *, unsigned char *, size_t width);
|
|
unsigned int m_rnd;
|
|
//#if defined(ON_SORT_TEMPLATE_TYPE) && !defined(ON_SORT_TEMPLATE_USE_SWAP)
|
|
// ON_SORT_TEMPLATE_TYPE m_tmp;
|
|
//#endif
|
|
|
|
CSorter(
|
|
DATATYPE *base
|
|
, size_t nel
|
|
, size_t width
|
|
, int (*compar)(const void *, const void *)
|
|
)
|
|
: m_base((DATATYPE*)base)
|
|
, m_nel(nel)
|
|
, m_width(width)
|
|
, m_compar(compar)
|
|
, m_rnd(62538161)
|
|
{
|
|
// When width is a multiple of 8 or 4 (with most arrays it probably is),
|
|
// use faster integer swappers instead of byte swapper
|
|
if ( 0 == width%sizeof(ON__UINT64))
|
|
m_swapfunc = SwapBigInts;
|
|
else if ( 0 == width%sizeof(ON__UINT32))
|
|
m_swapfunc = SwapInts;
|
|
else
|
|
m_swapfunc = SwapChars;
|
|
};
|
|
|
|
~CSorter() {};
|
|
|
|
DATATYPE* Pivot( DATATYPE* base, size_t count)
|
|
{
|
|
// Uses local quick and dirty pseudorandom number generator to
|
|
// give a fuzzy answer to avoid having the data be arranged in
|
|
// a way that mechanically always picking the pivot the same way
|
|
// affects the speed. Mostly affects chevron etc. patterns.
|
|
//
|
|
// Totally random pivot would guarantee O(nlogn) worst case, but
|
|
// does not perform as well on sorted or nearly sorted sets.
|
|
|
|
m_rnd *= 1664525;
|
|
m_rnd += 1013904223;
|
|
unsigned int dice = (m_rnd>>16)&7;
|
|
|
|
size_t p=count>>1; // 1/2
|
|
|
|
if ( dice&4)
|
|
p += count>>3; // +1/8
|
|
if ( dice&2)
|
|
p -= count>>4; // -1/16
|
|
if ( dice&1)
|
|
p -= count>>5; // -1/32
|
|
|
|
return base + p*DATAWIDTH;
|
|
}
|
|
|
|
void SortSmallRange( DATATYPE* p0, size_t count)
|
|
{
|
|
// use minimum compares and swaps for 2 to 4 items
|
|
switch (count)
|
|
{
|
|
case 2:
|
|
{
|
|
DATATYPE* p1 = p0 + DATAWIDTH;
|
|
if ( GreaterThan( p0, p1)) { Swap( p0, p1);}
|
|
return;
|
|
}
|
|
case 3:
|
|
{
|
|
DATATYPE* p1 = p0 + DATAWIDTH;
|
|
DATATYPE* p2 = p1 + DATAWIDTH;
|
|
bool b = false;
|
|
if ( GreaterThan( p0, p1)) { Swap( p0, p1); b = true;}
|
|
if ( GreaterThan( p1, p2)) { Swap( p1, p2); b = true;}
|
|
if ( b && GreaterThan( p0, p1)) { Swap( p0, p1);}
|
|
return;
|
|
}
|
|
case 4:
|
|
{
|
|
DATATYPE* p1 = p0 + DATAWIDTH;
|
|
DATATYPE* p2 = p1 + DATAWIDTH;
|
|
DATATYPE* p3 = p2 + DATAWIDTH;
|
|
if ( GreaterThan( p0, p3)) { Swap( p0, p3);}
|
|
if ( GreaterThan( p1, p2)) { Swap( p1, p2);}
|
|
bool b = false;
|
|
if ( GreaterThan( p2, p3)) { Swap( p2, p3); b = true;}
|
|
if ( GreaterThan( p0, p1)) { Swap( p0, p1); b = true;}
|
|
if ( b && GreaterThan( p1, p2)) { Swap( p1, p2);}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void SortRange( DATATYPE* left, DATATYPE* right)
|
|
{
|
|
while ( left<right)
|
|
{
|
|
size_t count = (right-left)/DATAWIDTH+1;
|
|
|
|
if ( count < 5)
|
|
return SortSmallRange( left, count);
|
|
|
|
DATATYPE* pivotleft;
|
|
DATATYPE* pivotright;
|
|
|
|
// partition range
|
|
{
|
|
pivotleft = Pivot( left, count);
|
|
|
|
// move pivot to left end
|
|
Swap( left, pivotleft);
|
|
|
|
pivotleft = left;
|
|
pivotright = right + DATAWIDTH;
|
|
|
|
// move =< pivot to left, and > pivot to right
|
|
for(;;)
|
|
{
|
|
// find next first item > pivot
|
|
pivotleft += DATAWIDTH;
|
|
if ( pivotleft >= pivotright)
|
|
break;
|
|
if ( !GreaterThan( pivotleft, left))
|
|
continue;
|
|
|
|
// find next last item =< pivot
|
|
do
|
|
{
|
|
pivotright -= DATAWIDTH;
|
|
if ( pivotleft >= pivotright)
|
|
goto END; // to quickly exit a nested loop
|
|
}
|
|
while( GreaterThan( pivotright, left));
|
|
|
|
Swap( pivotleft, pivotright);
|
|
}
|
|
|
|
END:
|
|
|
|
pivotright -= DATAWIDTH;
|
|
// move pivot to final place
|
|
Swap( left, pivotright);
|
|
pivotleft = pivotright;
|
|
|
|
// avoid overhead when not likely that there are multiple items == pivot
|
|
if ( pivotright >= right)
|
|
{
|
|
// the whole range is less or equal than pivot
|
|
// check if there are values == pivot left of it. Speeds up sorting arrays with all or lots of equal items.
|
|
for ( pivotleft -= DATAWIDTH; pivotleft > left; pivotleft -= DATAWIDTH)
|
|
{
|
|
if ( GreaterThan( pivotright, pivotleft))
|
|
break;
|
|
}
|
|
pivotleft += DATAWIDTH;
|
|
}
|
|
}
|
|
|
|
// limit max recursion depth to log(nel) by only recursing shorter part
|
|
if ( pivotleft-left < right-pivotright)
|
|
{
|
|
// lower part is shorter
|
|
SortRange( left, pivotleft-DATAWIDTH);
|
|
left = pivotright+DATAWIDTH;
|
|
}
|
|
else
|
|
{
|
|
// upper part is shorter
|
|
SortRange( pivotright+DATAWIDTH, right);
|
|
right = pivotleft-DATAWIDTH;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Sort()
|
|
{
|
|
SortRange( m_base, m_base + (m_nel-1)*DATAWIDTH);
|
|
}
|
|
};
|
|
|
|
if ( !base || nel < 2 )
|
|
return;
|
|
#if !defined(ON_SORT_TEMPLATE_TYPE)
|
|
if ( width < 1 || !compar)
|
|
return;
|
|
#endif
|
|
|
|
CSorter sorter( base, nel, width, compar);
|
|
sorter.Sort();
|
|
}
|
|
|
|
#undef Swap
|
|
#undef GreaterThan
|
|
#undef DATAWIDTH
|
|
#undef DATATYPE
|