Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/OgreMain/include/OgreRadixSort.h @ 1

Last change on this file since 1 was 1, checked in by landauf, 17 years ago
File size: 9.6 KB
Line 
1/*
2-----------------------------------------------------------------------------
3This source file is part of OGRE
4    (Object-oriented Graphics Rendering Engine)
5For the latest info, see http://www.ogre3d.org/
6
7Copyright (c) 2000-2006 Torus Knot Software Ltd
8Also see acknowledgements in Readme.html
9
10This program is free software; you can redistribute it and/or modify it under
11the terms of the GNU Lesser General Public License as published by the Free Software
12Foundation; either version 2 of the License, or (at your option) any later
13version.
14
15This program is distributed in the hope that it will be useful, but WITHOUT
16ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
18
19You should have received a copy of the GNU Lesser General Public License along with
20this program; if not, write to the Free Software Foundation, Inc., 59 Temple
21Place - Suite 330, Boston, MA 02111-1307, USA, or go to
22http://www.gnu.org/copyleft/lesser.txt.
23
24You may alternatively use this source under the terms of a specific version of
25the OGRE Unrestricted License provided you have obtained such a license from
26Torus Knot Software Ltd.
27-----------------------------------------------------------------------------
28*/
29#ifndef __RadixSort_H__
30#define __RadixSort_H__
31
32#include "OgrePrerequisites.h"
33
34namespace Ogre {
35
36        /** Class for performing a radix sort (fast comparison-less sort based on
37                byte value) on various standard STL containers.
38        @remarks
39                A radix sort is a very fast sort algorithm. It doesn't use comparisons
40                and thus is able to break the theoretical minimum O(N*logN) complexity.
41                Radix sort is complexity O(k*N), where k is a constant. Note that radix
42                sorting is not in-place, it requires additional storage, so it trades
43                memory for speed. The overhead of copying means that it is only faster
44                for fairly large datasets, so you are advised to only use it for collections
45                of at least a few hundred items.
46        @par
47                This is a template class to allow it to deal with a variety of containers,
48                and a variety of value types to sort on. In addition to providing the
49                container and value type on construction, you also need to supply a
50                functor object which will retrieve the value to compare on for each item
51                in the list. For example, if you had an std::vector of by-value instances
52                of an object of class 'Bibble', and you wanted to sort on
53                Bibble::getDoobrie(), you'd have to firstly create a functor
54                like this:
55        @code
56                struct BibbleSortFunctor
57                {
58                        float operator()(const Bibble& val) const
59                        {
60                                return val.getDoobrie();
61                        }
62                }
63        @endcode
64                Then, you need to declare a RadixSort class which names the container type,
65                the value type in the container, and the type of the value you want to
66                sort by. You can then call the sort function. E.g.
67        @code
68                RadixSort<BibbleList, Bibble, float> radixSorter;
69                BibbleSortFunctor functor;
70
71                radixSorter.sort(myBibbleList, functor);
72        @endcode
73                You should try to reuse RadixSort instances, since repeated allocation of the
74                internal storage is then avoided.
75        @note
76                Radix sorting is often associated with just unsigned integer values. Our
77                implementation can handle both unsigned and signed integers, as well as
78                floats (which are often not supported by other radix sorters). doubles
79                are not supported; you will need to implement your functor object to convert
80                to float if you wish to use this sort routine.
81        */
82        template <class TContainer, class TContainerValueType, typename TCompValueType>
83        class RadixSort
84        {
85        public:
86                typedef typename TContainer::iterator ContainerIter;
87        protected:
88                /// Alpha-pass counters of values (histogram)
89                /// 4 of them so we can radix sort a maximum of a 32bit value
90                int mCounters[4][256];
91                /// Beta-pass offsets
92                int mOffsets[256];
93                /// Sort area size
94                int mSortSize;
95                /// Number of passes for this type
96                int mNumPasses;
97
98                struct SortEntry
99                {
100                        TCompValueType key;
101                        ContainerIter iter;
102                        SortEntry() {}
103                        SortEntry(TCompValueType k, ContainerIter it)
104                                : key(k), iter(it) {}
105
106                };
107                /// Temp sort storage
108                std::vector<SortEntry> mSortArea1;
109                std::vector<SortEntry> mSortArea2;
110                std::vector<SortEntry>* mSrc;
111                std::vector<SortEntry>* mDest;
112                TContainer mTmpContainer; // initial copy
113
114
115                void sortPass(int byteIndex)
116                {
117                        // Calculate offsets
118                        // Basically this just leaves gaps for duplicate entries to fill
119                        mOffsets[0] = 0;
120                        for (int i = 1; i < 256; ++i)
121                        {
122                                mOffsets[i] = mOffsets[i-1] + mCounters[byteIndex][i-1];
123                        }
124
125                        // Sort pass
126                        for (int i = 0; i < mSortSize; ++i)
127                        {
128                                unsigned char byteVal = getByte(byteIndex, (*mSrc)[i].key);
129                                (*mDest)[mOffsets[byteVal]++] = (*mSrc)[i];
130                        }
131
132                }
133                template <typename T>
134                void finalPass(int byteIndex, T val)
135                {
136                        // default is to do normal pass
137                        sortPass(byteIndex);
138                }
139               
140                // special case signed int
141                void finalPass(int byteIndex, int val)
142                {
143                        int numNeg = 0;
144                        // all negative values are in entries 128+ in most significant byte
145                        for (int i = 128; i < 256; ++i)
146                        {
147                                numNeg += mCounters[byteIndex][i];
148                        }
149                        // Calculate offsets - positive ones start at the number of negatives
150                        // do positive numbers
151                        mOffsets[0] = numNeg;
152                        for (int i = 1; i < 128; ++i)
153                        {
154                                mOffsets[i] = mOffsets[i-1] + mCounters[byteIndex][i-1];
155                        }
156                        // Do negative numbers (must start at zero)
157                        // No need to invert ordering, already correct (-1 is highest number)
158                        mOffsets[128] = 0;
159                        for (int i = 129; i < 256; ++i)
160                        {
161                                mOffsets[i] = mOffsets[i-1] + mCounters[byteIndex][i-1];
162                        }
163
164                        // Sort pass
165                        for (int i = 0; i < mSortSize; ++i)
166                        {
167                                unsigned char byteVal = getByte(byteIndex, (*mSrc)[i].key);
168                                (*mDest)[mOffsets[byteVal]++] = (*mSrc)[i];
169                        }
170                }
171               
172
173                // special case float
174                void finalPass(int byteIndex, float val)
175                {
176                        // floats need to be special cased since negative numbers will come
177                        // after positives (high bit = sign) and will be in reverse order
178                        // (no ones-complement of the +ve value)
179                        int numNeg = 0;
180                        // all negative values are in entries 128+ in most significant byte
181                        for (int i = 128; i < 256; ++i)
182                        {
183                                numNeg += mCounters[byteIndex][i];
184                        }
185                        // Calculate offsets - positive ones start at the number of negatives
186                        // do positive numbers normally
187                        mOffsets[0] = numNeg;
188                        for (int i = 1; i < 128; ++i)
189                        {
190                                mOffsets[i] = mOffsets[i-1] + mCounters[byteIndex][i-1];
191                        }
192                        // Do negative numbers (must start at zero)
193                        // Also need to invert ordering
194                        // In order to preserve the stability of the sort (essential since
195                        // we rely on previous bytes already being sorted) we have to count
196                        // backwards in our offsets from
197                        mOffsets[255] = mCounters[byteIndex][255];
198                        for (int i = 254; i > 127; --i)
199                        {
200                                mOffsets[i] = mOffsets[i+1] + mCounters[byteIndex][i];
201                        }
202
203                        // Sort pass
204                        for (int i = 0; i < mSortSize; ++i)
205                        {
206                                unsigned char byteVal = getByte(byteIndex, (*mSrc)[i].key);
207                                if (byteVal > 127)
208                                {
209                                        // -ve; pre-decrement since offsets set to count
210                                        (*mDest)[--mOffsets[byteVal]] = (*mSrc)[i];
211                                }
212                                else
213                                {
214                                        // +ve
215                                        (*mDest)[mOffsets[byteVal]++] = (*mSrc)[i];
216                                }
217                        }
218                }
219
220                inline unsigned char getByte(int byteIndex, TCompValueType val)
221                {
222#if OGRE_ENDIAN == OGRE_ENDIAN_LITTLE
223                        return ((unsigned char*)(&val))[byteIndex];
224#else
225                        return ((unsigned char*)(&val))[mNumPasses - byteIndex - 1];
226#endif
227                }
228
229        public:
230
231                RadixSort() {}
232                ~RadixSort() {}
233
234                /** Main sort function
235                @param container A container of the type you declared when declaring
236                @param func A functor which returns the value for comparison when given
237                        a container value
238                */
239                template <class TFunction>
240                void sort(TContainer& container, TFunction func)
241                {
242                        if (container.empty())
243                                return;
244
245                        // Set up the sort areas
246                        mSortSize = static_cast<int>(container.size());
247                        mSortArea1.resize(container.size());
248                        mSortArea2.resize(container.size());
249
250                        // Copy data now (we need constant iterators for sorting)
251                        mTmpContainer = container;
252
253                        mNumPasses = sizeof(TCompValueType);
254
255                        // Counter pass
256                        // Initialise the counts
257                        int p;
258                        for (p = 0; p < mNumPasses; ++p)
259                                memset(mCounters[p], 0, sizeof(int) * 256);
260
261                        // Perform alpha pass to count
262                        ContainerIter i = mTmpContainer.begin();
263                        TCompValueType prevValue = func.operator()(*i); 
264                        bool needsSorting = false;
265                        for (int u = 0; i != mTmpContainer.end(); ++i, ++u)
266                        {
267                                // get sort value
268                                TCompValueType val = func.operator()(*i);
269                                // cheap check to see if needs sorting (temporal coherence)
270                                if (!needsSorting && val < prevValue)
271                                        needsSorting = true;
272
273                                // Create a sort entry
274                                mSortArea1[u].key = val;
275                                mSortArea1[u].iter = i;
276
277                                // increase counters
278                                for (p = 0; p < mNumPasses; ++p)
279                                {
280                                        unsigned char byteVal = getByte(p, val);
281                                        mCounters[p][byteVal]++;
282                                }
283
284                                prevValue = val;
285
286                        }
287
288                        // early exit if already sorted
289                        if (!needsSorting)
290                                return;
291
292
293                        // Sort passes
294                        mSrc = &mSortArea1;
295                        mDest = &mSortArea2;
296
297                        for (p = 0; p < mNumPasses - 1; ++p)
298                        {
299                                sortPass(p);
300                                // flip src/dst
301                                std::vector<SortEntry>* tmp = mSrc;
302                                mSrc = mDest;
303                                mDest = tmp;
304                        }
305                        // Final pass may differ, make polymorphic
306                        finalPass(p, prevValue);
307
308                        // Copy everything back
309                        int c = 0;
310                        for (i = container.begin(); 
311                                i != container.end(); ++i, ++c)
312                        {
313                                *i = *((*mDest)[c].iter);
314                        }
315                }
316
317        };
318
319
320}
321#endif
322
Note: See TracBrowser for help on using the repository browser.