]> pd.if.org Git - pdclib/blob - functions/stdlib/qsort.c
Added qsort.
[pdclib] / functions / stdlib / qsort.c
1 /* $Id$ */
2
3 /* Release $Name$ */
4
5 /* qsort( void *, size_t, size_t, int(*)( const void *, const void * ) )
6
7    This file is part of the Public Domain C Library (PDCLib).
8    Permission is granted to use, modify, and / or redistribute at will.
9 */
10
11 #include <stdlib.h>
12
13 #ifndef REGTEST
14
15 /* This implementation is taken from Paul Edward's PDPCLIB.
16
17    Original code is credited to Raymond Gardner, Englewood CO.
18    Minor mods are credited to Paul Edwards.
19    Some reformatting and simplification done by Martin Baute.
20    All code is still Public Domain.
21 */
22
23 /* Wrapper for _PDCLIB_memswp protects against multiple argument evaluation. */
24 static inline void memswp( char * i, char * j, unsigned int size )
25 {
26     _PDCLIB_memswp( i, j, size );
27 }
28
29 /* For small sets, insertion sort is faster than quicksort.
30    T is the threshold below which insertion sort will be used.
31    Must be 3 or larger.
32 */
33 #define T 7
34
35 /* Macros for handling the QSort stack */
36 #define PREPARE_STACK char * stack[STACKSIZE]; char * * stackptr = stack
37 #define PUSH( base, limit ) stackptr[0] = base; stackptr[1] = limit; stackptr += 2
38 #define POP( base, limit ) stackptr -= 2; base = stackptr[0]; limit = stackptr[1]
39 /* TODO: This is platform-dependent */
40 #define STACKSIZE 40
41
42 void qsort( void * base, size_t nmemb, size_t size, int (*compar)( const void *, const void * ) )
43 {
44     char * i;
45     char * j;
46     size_t thresh     = T * size;
47     char * base_      = (char *)base;
48     char * limit      = base_ + nmemb * size;
49     PREPARE_STACK;
50
51     for ( ;; )
52     {
53         if ( limit - base_ > thresh ) /* QSort for more than T elements. */
54         {
55             /* We work from second to last - first will be pivot element. */
56             i = base_ + size;
57             j = limit - size;
58             /* We swap first with middle element, then sort that with second
59                and last element so that eventually first element is the median
60                of the three - avoiding pathological pivots.
61                TODO: Instead of middle element, chose one randomly.
62             */
63             memswp( ( ( ( (size_t)( limit - base_ ) ) / size ) / 2 ) * size + base_, base_, size );
64             if ( compar( i, j ) > 0 ) memswp( i, j, size );
65             if ( compar( base_, j ) > 0 ) memswp( base_, j, size );
66             if ( compar( i, base_ ) > 0 ) memswp( i, base_, size );
67             /* Now we have the median for pivot element, entering main Quicksort. */
68             for ( ;; )
69             {
70                 do
71                 {
72                     /* move i right until *i >= pivot */
73                     i += size;
74                 } while ( compar( i, base_ ) < 0 );
75                 do
76                 {
77                     /* move j left until *j <= pivot */
78                     j -= size;
79                 } while ( compar( j, base_ ) > 0 );
80                 if ( i > j )
81                 {
82                     /* break loop if pointers crossed */
83                     break;
84                 }
85                 /* else swap elements, keep scanning */
86                 memswp( i, j, size );
87             }
88             /* move pivot into correct place */
89             memswp( base_, j, size );
90             /* larger subfile base / limit to stack, sort smaller */
91             if ( j - base_ > limit - i )
92             {
93                 /* left is larger */
94                 PUSH( base_, j );
95                 base_ = i;
96             }
97             else
98             {
99                 /* right is larger */
100                 PUSH( i, limit );
101                 limit = j;
102             }
103         }
104         else /* insertion sort for less than T elements              */
105         {
106             for ( j = base_, i = j + size; i < limit; j = i, i += size )
107             {
108                 for ( ; compar( j, j + size ) > 0; j -= size )
109                 {
110                     memswp( j, j + size, size );
111                     if ( j == base_ )
112                     {
113                         break;
114                     }
115                 }
116             }
117             if ( stackptr != stack )           /* if any entries on stack  */
118             {
119                 POP( base_, limit );
120             }
121             else                       /* else stack empty, done   */
122             {
123                 break;
124             }
125         }
126     }
127 }
128
129 #endif
130
131 #ifdef TEST
132 #include <_PDCLIB_test.h>
133 #include <string.h>
134 #include <limits.h>
135
136 int compare( const void * left, const void * right )
137 {
138     return *( (unsigned char *)left ) - *( (unsigned char *)right );
139 }
140
141 int main()
142 {
143     char presort[] = { "shreicnyjqpvozxmbt" };
144     char sorted1[] = { "bcehijmnopqrstvxyz" };
145     char sorted2[] = { "bticjqnyozpvreshxm" };
146     char s[19];
147     BEGIN_TESTS;
148     strcpy( s, presort );
149     qsort( s, 18, 1, compare );
150     TESTCASE( strcmp( s, sorted1 ) == 0 );
151     strcpy( s, presort );
152     qsort( s, 9, 2, compare );
153     TESTCASE( strcmp( s, sorted2 ) == 0 );
154     strcpy( s, presort );
155     qsort( s, 1, 1, compare );
156     TESTCASE( strcmp( s, presort ) == 0 );
157     qsort( s, 100, 0, compare );
158     TESTCASE( strcmp( s, presort ) == 0 );
159     return TEST_RESULTS;
160 }
161
162 #endif