e0fe87bd1f63f99bac1f09fe6d1ba9925e86eb64
[alexxy/gromacs.git] / src / gmxlib / gmx_sort.c
1 /* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
2  *
3  * 
4  * This file is part of Gromacs        Copyright (c) 1991-2010
5  * David van der Spoel, Erik Lindahl, Berk Hess, University of Groningen.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * To help us fund GROMACS development, we humbly ask that you cite
13  * the research papers on the package. Check out http://www.gromacs.org
14  * 
15  * And Hey:
16  * Gnomes, ROck Monsters And Chili Sauce
17  */
18
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22
23 #include <stdlib.h>
24 #include "gmx_sort.h"
25
26
27 static void 
28 qsort_swapfunc(void *        a,
29                void *        b,
30                size_t        n, 
31                int           swaptype)
32 {
33     int *  ia;
34     int *  ib;
35     int    itmp;
36     
37     char *  ca;
38     char *  cb;
39     char    ctmp;    
40     
41     if (swaptype <= 1) 
42     {
43         ia = (int *)a;
44         ib = (int *)b;
45         for( ; n > 0; ia += 1, ib += 1, n -= sizeof(int))
46         {
47             itmp      = *ia;
48             *ia       = *ib;
49             *ib       = itmp;
50         } 
51     } 
52     else 
53     {
54         ca = (char *)a;
55         cb = (char *)b;
56         for( ; n > 0; ca += 1, cb += 1, n -= 1)
57         {
58             ctmp       = *ca;
59             *ca        = *cb;
60             *cb        = ctmp;
61         }
62     }
63 }
64
65
66 static void *
67 qsort_med3(void *        a,
68            void *        b, 
69            void *        c, 
70            int          (*compar) (const void *a, const void *b))
71 {   
72     if(compar(a,b) < 0)
73     {
74         if(compar(b,c) < 0)
75             return b;
76         else if(compar(a,c) < 0)
77             return c;
78         else
79             return a;
80     }
81     else
82     {
83         if(compar(b,c) > 0)
84             return b;
85         else if(compar(a,c) > 0)
86             return c;
87         else
88             return a;
89     }
90 }
91
92
93 void
94 gmx_qsort(void *           base,
95           size_t           nmemb,
96           size_t           size,
97           int            (*compar)(const void *, const void *))
98 {
99 #define QSORT_EXCH(a, b, t) (t = a, a = b, b = t)
100 #define QSORT_SWAP(a, b) swaptype != 0 ? qsort_swapfunc(a, b, size, swaptype) : \
101                             (void)QSORT_EXCH(*(int *)(a), *(int *)(b), t)    
102     
103     char *pa, *pb, *pc, *pd, *pl, *pm, *pn, *pv, *cbase;
104     int r, swaptype;
105     int t, v;
106     size_t s, st;
107         
108     cbase = (char *)base;
109     
110     swaptype = (size_t)(cbase - (char *)0) % sizeof(int) || size % sizeof(int) ? 2 : size == sizeof(int)? 0 : 1;
111     
112     if (nmemb < 7)
113     {
114         /* Insertion sort on smallest arrays */
115         for (pm = cbase + size; pm < cbase + nmemb*size; pm += size)
116         {
117             for (pl = pm; (pl > cbase) && compar((void *)(pl-size),(void *) pl) > 0; pl -= size)
118             {
119                 QSORT_SWAP(pl, pl-size);
120             }
121         }
122         return;
123     }
124     
125     /* Small arrays, middle element */
126     pm = cbase + (nmemb/2)*size;      
127     
128     if (nmemb > 7)
129     {
130         pl = cbase;
131         pn = cbase + (nmemb-1)*size;
132         if (nmemb > 40)
133         {      
134             /* Big arrays, pseudomedian of 9 */
135             s = (nmemb/8)*size;
136             pl = (char *)qsort_med3((void *)pl, (void *)((size_t)pl+s), (void *)((size_t)pl+2*s), compar);
137             pm = (char *)qsort_med3((void *)((size_t)pm-s), (void *)pm, (void *)((size_t)pm+s), compar);
138             pn = (char *)qsort_med3((void *)((size_t)pn-2*s), (void *)((size_t)pn-s), (void *)pn, compar);
139         }
140         /* Mid-size, med of 3 */
141         pm = (char *)qsort_med3((void *)pl, (void *)pm, (void *)pn, compar);    
142     }
143     
144     /* pv points to partition value */
145     if (swaptype != 0) 
146     { 
147         pv = cbase;
148         QSORT_SWAP(pv, pm); 
149     } 
150     else 
151     {
152         pv = (char*)(void*)&v; 
153         v = *(int *)pm; 
154     }
155     
156     pa = pb = cbase;
157     pc = pd = cbase + (nmemb-1)*size;
158     
159     for (;;) 
160     {
161         while (pb <= pc && (r = compar((void *)pb,(void *) pv)) <= 0)
162         {
163             if (r == 0) 
164             { 
165                 QSORT_SWAP(pa, pb); 
166                 pa += size;
167             }
168             pb += size;
169         }
170         while (pc >= pb && (r = compar((void *)pc,(void *) pv)) >= 0) 
171         {
172             if (r == 0)
173             { 
174                 QSORT_SWAP(pc, pd); 
175                 pd -= size; 
176             }
177             pc -= size;
178         }
179         if (pb > pc) 
180         {
181             break;
182         }
183         QSORT_SWAP(pb, pc);
184         pb += size;
185         pc -= size;
186     }
187     pn = cbase + nmemb*size;
188     
189     s  = pa-cbase;
190     st = pb-pa;
191     if(st<s)
192     {
193         s = st;
194     }
195     
196     if(s>0)
197     {
198         qsort_swapfunc(cbase, pb-s, s, swaptype);
199     }
200     
201     s = pd-pc;
202     st = pn-pd-size;
203     if(st<s)
204     {
205         s = st;
206     }
207     
208     if(s>0)
209     {
210         qsort_swapfunc(pb, pn-s, s, swaptype);
211     }
212     
213     if ((s = pb-pa) > size) 
214     {
215         gmx_qsort(cbase, s/size, size, compar);
216     }
217     
218     if ((s = pd-pc) > size) 
219     {
220         gmx_qsort(pn-s, s/size, size, compar);
221     }
222     
223 #undef QSORT_EXCH
224 #undef QSORT_SWAP
225     
226     return;
227 }
228
229
230