├── LICENSE.md
├── README.md
├── plf_indiesort.h
└── plf_indiesort_test_suite.cpp
/LICENSE.md:
--------------------------------------------------------------------------------
1 | zlib License
2 |
3 | (C) 2019 mattreecebentley
4 |
5 | This software is provided 'as-is', without any express or implied
6 | warranty. In no event will the authors be held liable for any damages
7 | arising from the use of this software.
8 |
9 | Permission is granted to anyone to use this software for any purpose,
10 | including commercial applications, and to alter it and redistribute it
11 | freely, subject to the following restrictions:
12 |
13 | 1. The origin of this software must not be misrepresented; you must not
14 | claim that you wrote the original software. If you use this software
15 | in a product, an acknowledgment in the product documentation would be
16 | appreciated but is not required.
17 | 2. Altered source versions must be plainly marked as such, and must not be
18 | misrepresented as being the original software.
19 | 3. This notice may not be removed or altered from any source distribution.
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # plf_indiesort
2 | A sort wrapper enabling use of random-access (eg. std::sort) sorting on non-random access containers, and increased performance for the sorting of large types in random-access containers.
3 |
4 | It has a temporary memory cost of N * (sizeof(pointer) + sizeof(size_t)) for sorting non-random-access iterators/containers,
5 | and a N * sizeof(U) cost for random-access iterators/containers, where U = the smallest unsigned integer able to store N. For example if the size of the range being sorted is <= 255, U will be unsigned char.
6 |
7 | Indiesort should be used when:
8 |
9 | * The temporary memory cost mentioned is non-problematic,
10 | * The container or iterators are not random_access and therefore std::sort cannot be used, and/or
11 | * The element type is large or non-trivially-movable/copyable.
12 |
13 |
14 | It is, on average across all numbers of sorted elements:
15 |
16 | * +146% faster than std::sort when used on vectors or arrays of large structs (496 bytes). Crossover point for increased performance over std::sort is any type larger than 152 bytes.
17 | * +28% faster than std::list's internal sort, on types smaller than 272 bytes.
18 |
19 | std::list's internal sort is faster for types larger than 272 bytes (as it only writes previous and next pointers) and std::sort is faster on vectors and arrays for types smaller than 152 bytes.
20 | C++98/03/11/14/etc-compatible.
21 |
--------------------------------------------------------------------------------
/plf_indiesort.h:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2023, Matthew Bentley (mattreecebentley@gmail.com) www.plflib.org
2 |
3 | // zLib license (https://www.zlib.net/zlib_license.html):
4 | // This software is provided 'as-is', without any express or implied
5 | // warranty. In no event will the authors be held liable for any damages
6 | // arising from the use of this software.
7 | //
8 | // Permission is granted to anyone to use this software for any purpose,
9 | // including commercial applications, and to alter it and redistribute it
10 | // freely, subject to the following restrictions:
11 | //
12 | // 1. The origin of this software must not be misrepresented; you must not
13 | // claim that you wrote the original software. If you use this software
14 | // in a product, an acknowledgement in the product documentation would be
15 | // appreciated but is not required.
16 | // 2. Altered source versions must be plainly marked as such, and must not be
17 | // misrepresented as being the original software.
18 | // 3. This notice may not be removed or altered from any source distribution.
19 |
20 | #ifndef PLF_INDIESORT_H
21 | #define PLF_INDIESORT_H
22 |
23 |
24 | // Compiler-specific defines used by indiesort:
25 |
26 | // Define default cases before possibly redefining:
27 | #define PLF_CONSTFUNC
28 | #define PLF_NOEXCEPT throw()
29 | #define PLF_CONSTEXPR
30 |
31 | #if defined(_MSC_VER) && !defined(__clang__) && !defined(__GNUC__)
32 | // Suppress incorrect (unfixed MSVC bug) warnings re: constant expressions in constexpr-if statements
33 | #pragma warning ( push )
34 | #pragma warning ( disable : 4127 )
35 |
36 | #if _MSC_VER >= 1600
37 | #define PLF_DECLTYPE_SUPPORT
38 | #define PLF_MOVE_SEMANTICS_SUPPORT
39 | #endif
40 | #if _MSC_VER >= 1700
41 | #define PLF_TYPE_TRAITS_SUPPORT
42 | #define PLF_ALLOCATOR_TRAITS_SUPPORT
43 | #endif
44 | #if _MSC_VER >= 1800
45 | #define PLF_VARIADICS_SUPPORT // Variadics, in this context, means both variadic templates and variadic macros are supported
46 | #endif
47 | #if _MSC_VER >= 1900
48 | #undef PLF_NOEXCEPT
49 | #define PLF_NOEXCEPT noexcept
50 | #endif
51 |
52 | #if defined(_MSVC_LANG) && (_MSVC_LANG >= 201703L)
53 | #undef PLF_CONSTEXPR
54 | #define PLF_CONSTEXPR constexpr
55 | #endif
56 |
57 | #if defined(_MSVC_LANG) && (_MSVC_LANG >= 202002L) && _MSC_VER >= 1929
58 | #undef PLF_CONSTFUNC
59 | #define PLF_CONSTFUNC constexpr
60 | #endif
61 |
62 | #elif defined(__cplusplus) && __cplusplus >= 201103L // C++11 support, at least
63 | #define PLF_MOVE_SEMANTICS_SUPPORT
64 |
65 | #if defined(__GNUC__) && defined(__GNUC_MINOR__) && !defined(__clang__) // If compiler is GCC/G++
66 | #if (__GNUC__ == 4 && __GNUC_MINOR__ >= 3) || __GNUC__ > 4 // 4.2 and below do not support variadic templates or decltype
67 | #define PLF_VARIADICS_SUPPORT
68 | #define PLF_DECLTYPE_SUPPORT
69 | #endif
70 | #if (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4
71 | #undef PLF_NOEXCEPT
72 | #define PLF_NOEXCEPT noexcept
73 | #endif
74 | #if (__GNUC__ == 4 && __GNUC_MINOR__ >= 7) || __GNUC__ > 4
75 | #define PLF_ALLOCATOR_TRAITS_SUPPORT
76 | #endif
77 | #if __GNUC__ >= 5 // GCC v4.9 and below do not support std::is_trivially_copyable
78 | #define PLF_TYPE_TRAITS_SUPPORT
79 | #endif
80 | #elif defined(__clang__) && !defined(__GLIBCXX__) && !defined(_LIBCPP_CXX03_LANG)
81 | #if __clang_major__ >= 3 // clang versions < 3 don't support __has_feature() or traits
82 | #define PLF_ALLOCATOR_TRAITS_SUPPORT
83 | #define PLF_TYPE_TRAITS_SUPPORT
84 |
85 | #if __has_feature(cxx_decltype)
86 | #define PLF_DECLTYPE_SUPPORT
87 | #endif
88 | #if __has_feature(cxx_noexcept)
89 | #undef PLF_NOEXCEPT
90 | #define PLF_NOEXCEPT noexcept
91 | #endif
92 | #if __has_feature(cxx_rvalue_references) && !defined(_LIBCPP_HAS_NO_RVALUE_REFERENCES)
93 | #define PLF_MOVE_SEMANTICS_SUPPORT
94 | #endif
95 | #if __has_feature(cxx_variadic_templates) && !defined(_LIBCPP_HAS_NO_VARIADICS)
96 | #define PLF_VARIADICS_SUPPORT
97 | #endif
98 | #endif
99 | #elif defined(__GLIBCXX__) // Using another compiler type with libstdc++ - we are assuming full c++11 compliance for compiler - which may not be true
100 | #define PLF_DECLTYPE_SUPPORT
101 |
102 | #if __GLIBCXX__ >= 20080606 // libstdc++ 4.2 and below do not support variadic templates
103 | #define PLF_VARIADICS_SUPPORT
104 | #endif
105 | #if __GLIBCXX__ >= 20120322
106 | #define PLF_ALLOCATOR_TRAITS_SUPPORT
107 | #undef PLF_NOEXCEPT
108 | #define PLF_NOEXCEPT noexcept
109 | #endif
110 | #if __GLIBCXX__ >= 20150422 // libstdc++ v4.9 and below do not support std::is_trivially_copyable
111 | #define PLF_TYPE_TRAITS_SUPPORT
112 | #endif
113 | #elif defined(_LIBCPP_CXX03_LANG) // Special case for checking C++11 support with libcpp
114 | #if !defined(_LIBCPP_HAS_NO_VARIADICS)
115 | #define PLF_VARIADICS_SUPPORT
116 | #endif
117 | #else // Assume full support for other compilers and standard libraries
118 | #define PLF_DECLTYPE_SUPPORT
119 | #define PLF_INITIALIZER_LIST_SUPPORT
120 | #define PLF_ALLOCATOR_TRAITS_SUPPORT
121 | #define PLF_VARIADICS_SUPPORT
122 | #define PLF_TYPE_TRAITS_SUPPORT
123 | #undef PLF_NOEXCEPT
124 | #define PLF_NOEXCEPT noexcept
125 | #endif
126 |
127 | #if __cplusplus >= 201703L && ((defined(__clang__) && ((__clang_major__ == 3 && __clang_minor__ == 9) || __clang_major__ > 3)) || (defined(__GNUC__) && __GNUC__ >= 7) || (!defined(__clang__) && !defined(__GNUC__)))
128 | #undef PLF_CONSTEXPR
129 | #define PLF_CONSTEXPR constexpr
130 | #endif
131 |
132 | #if __cplusplus > 201704L && ((((defined(__clang__) && !defined(__APPLE_CC__) && __clang_major__ >= 14) || (defined(__GNUC__) && (__GNUC__ > 11 || (__GNUC__ == 11 && __GNUC_MINOR__ > 0)))) && ((defined(_LIBCPP_VERSION) && _LIBCPP_VERSION >= 14) || (defined(__GLIBCXX__) && __GLIBCXX__ >= 201806L))) || (!defined(__clang__) && !defined(__GNUC__)))
133 | #define PLF_CPP20_SUPPORT
134 | #undef PLF_CONSTFUNC
135 | #define PLF_CONSTFUNC constexpr
136 | #endif
137 | #endif
138 |
139 |
140 |
141 |
142 | #ifdef PLF_ALLOCATOR_TRAITS_SUPPORT
143 | #ifdef PLF_VARIADICS_SUPPORT
144 | #define PLF_CONSTRUCT(the_allocator, allocator_instance, location, ...) std::allocator_traits::construct(allocator_instance, location, __VA_ARGS__)
145 | #else
146 | #define PLF_CONSTRUCT(the_allocator, allocator_instance, location, data) std::allocator_traits::construct(allocator_instance, location, data)
147 | #endif
148 |
149 | #define PLF_DESTROY(the_allocator, allocator_instance, location) std::allocator_traits::destroy(allocator_instance, location)
150 | #define PLF_ALLOCATE(the_allocator, allocator_instance, size, hint) std::allocator_traits::allocate(allocator_instance, size, hint)
151 | #define PLF_DEALLOCATE(the_allocator, allocator_instance, location, size) std::allocator_traits::deallocate(allocator_instance, location, size)
152 | #else
153 | #ifdef PLF_VARIADICS_SUPPORT
154 | #define PLF_CONSTRUCT(the_allocator, allocator_instance, location, ...) (allocator_instance).construct(location, __VA_ARGS__)
155 | #else
156 | #define PLF_CONSTRUCT(the_allocator, allocator_instance, location, data) (allocator_instance).construct(location, data)
157 | #endif
158 |
159 | #define PLF_DESTROY(the_allocator, allocator_instance, location) (allocator_instance).destroy(location)
160 | #define PLF_ALLOCATE(the_allocator, allocator_instance, size, hint) (allocator_instance).allocate(size, hint)
161 | #define PLF_DEALLOCATE(the_allocator, allocator_instance, location, size) (allocator_instance).deallocate(location, size)
162 | #endif
163 |
164 |
165 |
166 | #ifndef PLF_SORT_FUNCTION
167 | #include // std::sort
168 | #define PLF_SORT_FUNCTION std::sort
169 | #define PLF_SORT_FUNCTION_DEFINED
170 | #endif
171 |
172 | #include // assert
173 | #include // std::size_t
174 | #include // std::allocator, std::to_address
175 | #include // std::numeric_limits
176 | #include // std::iterator_traits, std::move_iterator, std::distance
177 |
178 |
179 | #ifdef PLF_TYPE_TRAITS_SUPPORT
180 | #include // is_same
181 | #endif
182 |
183 | #ifdef PLF_MOVE_SEMANTICS_SUPPORT
184 | #include // std::move
185 | #endif
186 |
187 |
188 |
189 | namespace plf
190 | {
191 | // std:: tool replacements for C++03/98/11 support:
192 |
193 | #ifndef PLF_TOOLS
194 | #define PLF_TOOLS
195 |
196 | template
197 | struct enable_if
198 | {
199 | typedef T type;
200 | };
201 |
202 | template
203 | struct enable_if
204 | {};
205 |
206 |
207 |
208 | template struct conditional;
209 |
210 | template struct conditional
211 | {
212 | typedef is_true type;
213 | };
214 |
215 | template struct conditional
216 | {
217 | typedef is_false type;
218 | };
219 |
220 |
221 |
222 | template
223 | struct less
224 | {
225 | bool operator() (const element_type &a, const element_type &b) const PLF_NOEXCEPT
226 | {
227 | return a < b;
228 | }
229 | };
230 |
231 |
232 |
233 | template
234 | struct equal_to
235 | {
236 | const element_type value;
237 |
238 | explicit equal_to(const element_type store_value): // no noexcept as element may allocate and potentially throw when copied
239 | value(store_value)
240 | {}
241 |
242 | bool operator() (const element_type compare_value) const PLF_NOEXCEPT
243 | {
244 | return value == compare_value;
245 | }
246 | };
247 |
248 |
249 |
250 | // To enable conversion to void * when allocator supplies non-raw pointers:
251 | template
252 | static PLF_CONSTFUNC void * void_cast(const source_pointer_type source_pointer) PLF_NOEXCEPT
253 | {
254 | #if defined(PLF_CPP20_SUPPORT)
255 | return static_cast(std::to_address(source_pointer));
256 | #else
257 | return static_cast(&*source_pointer);
258 | #endif
259 | }
260 |
261 |
262 |
263 | #ifdef PLF_MOVE_SEMANTICS_SUPPORT
264 | template
265 | PLF_CONSTFUNC std::move_iterator make_move_iterator(iterator_type it)
266 | {
267 | return std::move_iterator(std::move(it));
268 | }
269 | #endif
270 |
271 |
272 |
273 | enum priority { performance = 1, memory_use = 4};
274 | #endif
275 |
276 |
277 | template
278 | struct is_pointer
279 | {
280 | static const bool value = false;
281 | };
282 |
283 | template
284 | struct is_pointer
285 | {
286 | static const bool value = true;
287 | };
288 |
289 |
290 |
291 | template< class T > struct remove_pointer {typedef T type;};
292 | template< class T > struct remove_pointer {typedef T type;};
293 | template< class T > struct remove_pointer {typedef T type;};
294 | template< class T > struct remove_pointer {typedef T type;};
295 | template< class T > struct remove_pointer {typedef T type;};
296 |
297 |
298 |
299 | template struct derive_type;
300 |
301 | template struct derive_type
302 | {
303 | typedef typename remove_pointer::type type;
304 | };
305 |
306 | template struct derive_type
307 | {
308 | typedef typename the_type::value_type type;
309 | };
310 |
311 |
312 |
313 | // Function-object, used to redirect the sort function to compare element pointers by the elements they point to, and sort the element pointers instead of the elements:
314 |
315 | template
316 | struct random_access_sort_dereferencer
317 | {
318 | comparison_function stored_instance;
319 | const iterator_type stored_first_iterator;
320 |
321 | PLF_CONSTFUNC explicit random_access_sort_dereferencer(const comparison_function &function_instance, const iterator_type first):
322 | stored_instance(function_instance),
323 | stored_first_iterator(first)
324 | {}
325 |
326 | PLF_CONSTFUNC bool operator() (const size_type index1, const size_type index2)
327 | {
328 | return stored_instance(*(stored_first_iterator + index1), *(stored_first_iterator + index2));
329 | }
330 | };
331 |
332 |
333 |
334 | template
335 | PLF_CONSTFUNC void random_access_sort(const iterator_type first, comparison_function compare, const size_type size)
336 | {
337 | typedef typename derive_type::value, iterator_type>::type element_type;
338 | typedef typename std::allocator size_type_allocator_type;
339 |
340 | size_type_allocator_type size_type_allocator;
341 | size_type * const sort_array = PLF_ALLOCATE(size_type_allocator_type, size_type_allocator, size, NULL);
342 | size_type *size_type_pointer = sort_array;
343 |
344 | // Construct pointers to all elements in the sequence:
345 | for (size_type index = 0; index != size; ++index, ++size_type_pointer)
346 | {
347 | PLF_CONSTRUCT(size_type_allocator_type, size_type_allocator, size_type_pointer, index);
348 | }
349 |
350 | // Now, sort the pointers by the values they point to (std::sort is default sort function if the macro below is not defined prior to header inclusion):
351 | PLF_SORT_FUNCTION(sort_array, size_type_pointer, random_access_sort_dereferencer(compare, first));
352 |
353 | // Sort the actual elements via the tuple array:
354 | size_type index = 0;
355 |
356 | for (size_type *current_index = sort_array; current_index != size_type_pointer; ++current_index, ++index)
357 | {
358 | if (*current_index != index)
359 | {
360 | size_type destination_index = index;
361 |
362 | #ifdef PLF_MOVE_SEMANTICS_SUPPORT
363 | element_type end_value = std::move(*(first + destination_index));
364 | #else
365 | element_type end_value = *(first + destination_index);
366 | #endif
367 |
368 | size_type source_index = *current_index;
369 |
370 | do
371 | {
372 | #ifdef PLF_MOVE_SEMANTICS_SUPPORT
373 | *(first + destination_index) = std::move(*(first + source_index));
374 | #else
375 | *(first + destination_index) = *(first + source_index);
376 | #endif
377 |
378 | destination_index = source_index;
379 | source_index = sort_array[destination_index];
380 | sort_array[destination_index] = destination_index;
381 | } while (source_index != index);
382 |
383 | #ifdef PLF_MOVE_SEMANTICS_SUPPORT
384 | *(first + destination_index) = std::move(end_value);
385 | #else
386 | *(first + destination_index) = end_value;
387 | #endif
388 | }
389 | }
390 |
391 | PLF_DEALLOCATE(size_type_allocator_type, size_type_allocator, sort_array, size);
392 | }
393 |
394 |
395 |
396 | template
397 | PLF_CONSTFUNC void call_random_access_sort(const iterator_type first, const iterator_type last, comparison_function compare)
398 | {
399 | assert(first <= last);
400 | typedef typename derive_type::value, iterator_type>::type element_type;
401 |
402 | #ifdef PLF_TYPE_TRAITS_SUPPORT
403 | if PLF_CONSTEXPR ((std::is_trivially_copyable::value || std::is_move_assignable::value) && sizeof(element_type) < 152) // ie. below benchmarked threshold for improved performance over direct std::sort
404 | #else
405 | if PLF_CONSTEXPR (sizeof(element_type) < 152)
406 | #endif
407 | {
408 | PLF_SORT_FUNCTION(first, last, compare);
409 | return;
410 | }
411 |
412 |
413 | const std::size_t size = static_cast(last - first);
414 |
415 | if (size < 2)
416 | {
417 | return;
418 | }
419 | else if (size <= std::numeric_limits::max())
420 | {
421 | random_access_sort(first, compare, static_cast(size));
422 | }
423 | else if (size <= std::numeric_limits::max())
424 | {
425 | random_access_sort(first, compare, static_cast(size));
426 | }
427 | else if (size <= std::numeric_limits::max())
428 | {
429 | random_access_sort(first, compare, static_cast(size));
430 | }
431 | else
432 | {
433 | random_access_sort(first, compare, size);
434 | }
435 | }
436 |
437 |
438 |
439 | template
440 | struct sort_dereferencer
441 | {
442 | comparison_function stored_instance;
443 |
444 | PLF_CONSTFUNC explicit sort_dereferencer(const comparison_function &function_instance):
445 | stored_instance(function_instance)
446 | {}
447 |
448 | PLF_CONSTFUNC bool operator() (const element_type first, const element_type second)
449 | {
450 | return stored_instance(*(first.original_location), *(second.original_location));
451 | }
452 | };
453 |
454 |
455 |
456 | // This struct must be non-local to the function below for C++03 compatibility:
457 |
458 | template
459 | struct pointer_index_tuple
460 | {
461 | pointer original_location;
462 | size_type original_index;
463 |
464 | PLF_CONSTFUNC pointer_index_tuple(const pointer _item, const size_type _index) PLF_NOEXCEPT:
465 | original_location(_item),
466 | original_index(_index)
467 | {}
468 | };
469 |
470 |
471 |
472 | template
473 | PLF_CONSTFUNC void non_random_access_sort(const iterator_type first, const iterator_type last, comparison_function compare, const std::size_t size)
474 | {
475 | if (size < 2)
476 | {
477 | return;
478 | }
479 |
480 | typedef typename derive_type::value, iterator_type>::type element_type;
481 |
482 | #ifdef PLF_TYPE_TRAITS_SUPPORT
483 | if PLF_CONSTEXPR ((std::is_trivially_copyable::value || std::is_move_assignable::value) && sizeof(element_type) <= sizeof(element_type *) * 2) // If element is <= 2 pointers, just copy to an array and sort that then copy back - consumes less memory and may be faster
484 | #else
485 | if PLF_CONSTEXPR (sizeof(element_type) <= sizeof(element_type *) * 2)
486 | #endif
487 | {
488 | typedef typename std::allocator allocator_type;
489 | allocator_type alloc;
490 | element_type * const sort_array = PLF_ALLOCATE(allocator_type, alloc, size, NULL), * const end = sort_array + size;
491 |
492 | #ifdef PLF_TYPE_TRAITS_SUPPORT
493 | if PLF_CONSTEXPR (!std::is_trivially_copyable::value && std::is_move_assignable::value)
494 | {
495 | std::uninitialized_copy(plf::make_move_iterator(first), plf::make_move_iterator(last), sort_array);
496 | }
497 | else
498 | #endif
499 | {
500 | std::uninitialized_copy(first, last, sort_array);
501 | }
502 |
503 | PLF_SORT_FUNCTION(sort_array, end, compare);
504 |
505 | #ifdef PLF_TYPE_TRAITS_SUPPORT
506 | if PLF_CONSTEXPR (!std::is_trivially_copyable::value && std::is_move_assignable::value)
507 | {
508 | std::copy(plf::make_move_iterator(sort_array), plf::make_move_iterator(end), first);
509 | }
510 | else
511 | #endif
512 | {
513 | std::copy(sort_array, end, first);
514 |
515 | #ifdef PLF_TYPE_TRAITS_SUPPORT
516 | if (!std::is_trivially_destructible::value)
517 | #endif
518 | {
519 | for (element_type *current = sort_array; current != end; ++current)
520 | {
521 | PLF_DESTROY(allocator_type, alloc, current);
522 | }
523 | }
524 | }
525 |
526 | PLF_DEALLOCATE(allocator_type, alloc, sort_array, size);
527 | return;
528 | }
529 | else
530 | {
531 | typedef typename std::size_t size_type;
532 | typedef pointer_index_tuple item_index_tuple;
533 |
534 | typedef typename std::allocator tuple_allocator_type;
535 | tuple_allocator_type tuple_allocator;
536 |
537 | item_index_tuple * const sort_array = PLF_ALLOCATE(tuple_allocator_type, tuple_allocator, size, NULL);
538 | item_index_tuple *tuple_pointer = sort_array;
539 |
540 | // Construct pointers to all elements in the sequence:
541 | size_type index = 0;
542 |
543 | for (iterator_type current_element = first; current_element != last; ++current_element, ++tuple_pointer, ++index)
544 | {
545 | #ifdef PLF_VARIADICS_SUPPORT
546 | PLF_CONSTRUCT(tuple_allocator_type, tuple_allocator, tuple_pointer, &*current_element, index);
547 | #else
548 | PLF_CONSTRUCT(tuple_allocator_type, tuple_allocator, tuple_pointer, item_index_tuple(&*current_element, index));
549 | #endif
550 | }
551 |
552 | PLF_SORT_FUNCTION(sort_array, sort_array + size, sort_dereferencer(compare));
553 |
554 | // Sort the actual elements via the tuple array:
555 | index = 0;
556 |
557 | for (item_index_tuple *current_tuple = sort_array; current_tuple != tuple_pointer; ++current_tuple, ++index)
558 | {
559 | if (current_tuple->original_index != index)
560 | {
561 | #ifdef PLF_MOVE_SEMANTICS_SUPPORT
562 | element_type end_value = std::move(*(current_tuple->original_location));
563 | #else
564 | element_type end_value = *(current_tuple->original_location);
565 | #endif
566 |
567 | size_type destination_index = index;
568 | size_type source_index = current_tuple->original_index;
569 |
570 | do
571 | {
572 | #ifdef PLF_MOVE_SEMANTICS_SUPPORT
573 | *(sort_array[destination_index].original_location) = std::move(*(sort_array[source_index].original_location));
574 | #else
575 | *(sort_array[destination_index].original_location) = *(sort_array[source_index].original_location);
576 | #endif
577 |
578 | destination_index = source_index;
579 | source_index = sort_array[destination_index].original_index;
580 | sort_array[destination_index].original_index = destination_index;
581 | } while (source_index != index);
582 |
583 | #ifdef PLF_MOVE_SEMANTICS_SUPPORT
584 | *(sort_array[destination_index].original_location) = std::move(end_value);
585 | #else
586 | *(sort_array[destination_index].original_location) = end_value;
587 | #endif
588 | }
589 | }
590 |
591 | PLF_DEALLOCATE(tuple_allocator_type, tuple_allocator, sort_array, size);
592 | }
593 | }
594 |
595 |
596 |
597 | // Range templates:
598 | template
599 | PLF_CONSTFUNC void indiesort(const iterator_type first, const iterator_type last, comparison_function compare, const std::size_t size)
600 | {
601 | non_random_access_sort(first, last, compare, size);
602 | }
603 |
604 |
605 |
606 | template
607 | #ifdef PLF_TYPE_TRAITS_SUPPORT
608 | PLF_CONSTFUNC void indiesort(const typename plf::enable_if::value || std::is_same::iterator_category, std::random_access_iterator_tag>::value), iterator_type>::type first, const iterator_type last, comparison_function compare)
609 | #else
610 | PLF_CONSTFUNC void indiesort(const typename plf::enable_if::value, iterator_type>::type first, const iterator_type last, comparison_function compare)
611 | #endif
612 | {
613 | non_random_access_sort(first, last, compare, std::distance(first, last));
614 | }
615 |
616 |
617 |
618 | template
619 | #ifdef PLF_TYPE_TRAITS_SUPPORT
620 | PLF_CONSTFUNC void indiesort(const typename plf::enable_if<(is_pointer::value || std::is_same::iterator_category, std::random_access_iterator_tag>::value), iterator_type>::type first, const iterator_type last, comparison_function compare)
621 | #else
622 | PLF_CONSTFUNC void indiesort(const typename plf::enable_if::value, iterator_type>::type first, const iterator_type last, comparison_function compare)
623 | #endif
624 | {
625 | call_random_access_sort(first, last, compare);
626 | }
627 |
628 |
629 |
630 | template
631 | PLF_CONSTFUNC void indiesort(const iterator_type first, const iterator_type last)
632 | {
633 | indiesort(first, last, plf::less::value, iterator_type>::type>());
634 | }
635 |
636 |
637 |
638 | // Container-based templates:
639 |
640 | #ifdef PLF_TYPE_TRAITS_SUPPORT
641 | template ::iterator_category, std::random_access_iterator_tag>::value, container_type>::type * = nullptr>
642 | PLF_CONSTFUNC void indiesort(container_type &container, comparison_function compare)
643 | {
644 | call_random_access_sort(container.begin(), container.end(), compare);
645 | }
646 | #endif
647 |
648 |
649 |
650 | #ifdef PLF_DECLTYPE_SUPPORT
651 | template
652 | class has_size_function
653 | {
654 | private:
655 | typedef char one;
656 | struct two { char x[2]; };
657 |
658 | template PLF_CONSTFUNC static one test( decltype(&C::size) ) ;
659 | template PLF_CONSTFUNC static two test(...);
660 |
661 | public:
662 | enum { value = sizeof(test(0)) == sizeof(char) };
663 | };
664 | #endif
665 |
666 |
667 |
668 | #ifdef PLF_TYPE_TRAITS_SUPPORT
669 | template ::iterator_category, std::random_access_iterator_tag>::value, container_type>::type * = nullptr>
670 | #else
671 | template
672 | #endif
673 | PLF_CONSTFUNC void indiesort(container_type &container, comparison_function compare)
674 | {
675 | #ifdef PLF_DECLTYPE_SUPPORT
676 | if PLF_CONSTEXPR (has_size_function::value)
677 | {
678 | non_random_access_sort(container.begin(), container.end(), compare, static_cast(container.size()));
679 | }
680 | else
681 | #endif
682 | { // If no decltype support, assume container doesn't have size()
683 | indiesort(container.begin(), container.end(), compare); // call range indiesort
684 | }
685 | }
686 |
687 |
688 |
689 | template
690 | PLF_CONSTFUNC void indiesort(container_type &container)
691 | {
692 | indiesort(container, plf::less());
693 | }
694 |
695 | }
696 |
697 |
698 | #undef PLF_DECLTYPE_SUPPORT
699 | #undef PLF_TYPE_TRAITS_SUPPORT
700 | #undef PLF_ALLOCATOR_TRAITS_SUPPORT
701 | #undef PLF_VARIADICS_SUPPORT
702 | #undef PLF_MOVE_SEMANTICS_SUPPORT
703 | #undef PLF_NOEXCEPT
704 | #undef PLF_CONSTEXPR
705 | #undef PLF_CONSTFUNC
706 |
707 | #undef PLF_CONSTRUCT
708 | #undef PLF_ALLOCATE
709 | #undef PLF_DEALLOCATE
710 |
711 | #if defined(_MSC_VER) && !defined(__clang__) && !defined(__GNUC__)
712 | #pragma warning ( pop )
713 | #endif
714 |
715 | #ifdef PLF_SORT_FUNCTION_DEFINED
716 | #undef PLF_SORT_FUNCTION
717 | #undef PLF_SORT_FUNCTION_DEFINED
718 | #endif
719 |
720 |
721 | #endif // PLF_INDIESORT_H
722 |
--------------------------------------------------------------------------------
/plf_indiesort_test_suite.cpp:
--------------------------------------------------------------------------------
1 | #if defined(_MSC_VER)
2 | #if _MSC_VER >= 1600
3 | #define PLF_TEST_MOVE_SEMANTICS_SUPPORT
4 | #endif
5 | #elif defined(__cplusplus) && __cplusplus >= 201103L // C++11 support, at least
6 | #define PLF_TEST_MOVE_SEMANTICS_SUPPORT
7 | #endif
8 |
9 |
10 | #include // std::greater
11 | #include
12 | #include
13 | #include
14 |
15 | #include "plf_indiesort.h"
16 |
17 |
18 |
19 | void title1(const char *title_text)
20 | {
21 | std::cout << std::endl << std::endl << std::endl << "*** " << title_text << " ***" << std::endl;
22 | std::cout << "===========================================" << std::endl << std::endl << std::endl;
23 | }
24 |
25 |
26 |
27 | void title2(const char *title_text)
28 | {
29 | std::cout << std::endl << std::endl << "--- " << title_text << " ---" << std::endl << std::endl;
30 | }
31 |
32 |
33 |
34 | void breakfail(const char *error_message)
35 | {
36 | std::cout << error_message << std::endl;
37 | std::cin.get();
38 | abort();
39 | }
40 |
41 |
42 |
43 | struct small_struct
44 | {
45 | double *empty_field_1;
46 | double unused_number;
47 | unsigned int empty_field2;
48 | double *empty_field_3;
49 | int number;
50 | unsigned int empty_field4;
51 |
52 | small_struct() : number(0) {};
53 | small_struct(const int num) : number(num) {};
54 | int operator * () const { return number; };
55 | bool operator == (const small_struct &source) const { return source.number == number; };
56 | bool operator != (const small_struct &source) const { return source.number != number; };
57 | bool operator > (const small_struct &source) const { return number > source.number; };
58 | bool operator < (const small_struct &source) const { return number < source.number; };
59 | bool operator >= (const small_struct &source) const { return number >= source.number; };
60 | bool operator <= (const small_struct &source) const { return number <= source.number; };
61 | };
62 |
63 |
64 |
65 | int global_counter = 0;
66 |
67 | struct small_struct_non_trivial
68 | {
69 | double *empty_field_1;
70 | double unused_number;
71 | unsigned int empty_field2;
72 | double *empty_field_3;
73 | int number;
74 | unsigned int empty_field4;
75 |
76 | small_struct_non_trivial() : number(0) {};
77 | small_struct_non_trivial(const int num) : number(num) {};
78 | small_struct_non_trivial(const small_struct_non_trivial &source) : number(source.number) {};
79 | small_struct_non_trivial operator = (small_struct_non_trivial &source) { number = source.number; return *this; };
80 |
81 | #ifdef PLF_TEST_MOVE_SEMANTICS_SUPPORT
82 | small_struct_non_trivial(small_struct_non_trivial &&source) : number(std::move(source.number)) {};
83 | small_struct_non_trivial operator = (small_struct_non_trivial &&source) { number = std::move(source.number); return *this; };
84 | #endif
85 |
86 | int operator * () const { return number; };
87 | bool operator == (const small_struct_non_trivial &source) const { return source.number == number; };
88 | bool operator != (const small_struct_non_trivial &source) const { return source.number != number; };
89 | bool operator > (const small_struct_non_trivial &source) const { return number > source.number; };
90 | bool operator < (const small_struct_non_trivial &source) const { return number < source.number; };
91 | bool operator >= (const small_struct_non_trivial &source) const { return number >= source.number; };
92 | bool operator <= (const small_struct_non_trivial &source) const { return number <= source.number; };
93 | };
94 |
95 |
96 |
97 | struct large_struct
98 | {
99 | int numbers[100];
100 | char a_string[50];
101 | double unused_number;
102 | int number;
103 | double *empty_field_1;
104 | double *empty_field_2;
105 | unsigned int empty_field3;
106 | unsigned int empty_field4;
107 |
108 | large_struct() : number(0) {};
109 | large_struct(const int num) : number(num) {};
110 | int operator * () const { return number; };
111 | bool operator == (const large_struct &source) const { return source.number == number; };
112 | bool operator != (const large_struct &source) const { return source.number != number; };
113 | bool operator > (const large_struct &source) const { return number > source.number; };
114 | bool operator < (const large_struct &source) const { return number < source.number; };
115 | bool operator >= (const large_struct &source) const { return number >= source.number; };
116 | bool operator <= (const large_struct &source) const { return number <= source.number; };
117 | };
118 |
119 |
120 |
121 |
122 | int main()
123 | {
124 | freopen("error.log","w", stderr); // For catching assertion failure info when run outside of a command line prompt
125 |
126 | unsigned int looper = 0;
127 |
128 | while (++looper != 600)
129 | {
130 | title1("Indiesort");
131 |
132 | {
133 | title2("Test Basics Int");
134 |
135 | std::vector vec;
136 |
137 | for (int i = 60; i != -1; --i)
138 | {
139 | vec.push_back(i);
140 | }
141 |
142 | for (int i = 0; i != 60; ++i)
143 | {
144 | vec.push_back(i);
145 | }
146 |
147 | plf::indiesort(vec);
148 |
149 | int last = 0;
150 |
151 | for (std::vector::iterator it = vec.begin(); it != vec.end(); ++it)
152 | {
153 | if (last > *it)
154 | {
155 | breakfail("Sequential int last > *it!.");
156 | }
157 |
158 | last = *it;
159 | }
160 |
161 | std::cout << "Sequential int passed\n";
162 |
163 | vec.clear();
164 |
165 |
166 | for (int i = 0; i != 70000; ++i)
167 | {
168 | vec.push_back(rand() & 65535);
169 | }
170 |
171 | plf::indiesort(vec, std::greater());
172 |
173 | last = 65535;
174 |
175 | for (std::vector::iterator it = vec.begin(); it != vec.end(); ++it)
176 | {
177 | if (last < *it)
178 | {
179 | breakfail("Random int last < *it!.");
180 | }
181 |
182 | last = *it;
183 | }
184 |
185 | std::cout << "Random int passed\n";
186 | }
187 |
188 |
189 | {
190 | title2("Test Basics small_struct");
191 |
192 | std::vector vec;
193 |
194 | for (int i = 60; i != -1; --i)
195 | {
196 | vec.push_back(i);
197 | }
198 |
199 | for (int i = 0; i != 60; ++i)
200 | {
201 | vec.push_back(i);
202 | }
203 |
204 | plf::indiesort(vec);
205 |
206 | small_struct last = 0;
207 |
208 | for (std::vector::iterator it = vec.begin(); it != vec.end(); ++it)
209 | {
210 | if (last > **it)
211 | {
212 | breakfail("Sequential small_struct last > *it!.");
213 | }
214 |
215 | last = **it;
216 | }
217 |
218 | std::cout << "Sequential Small Struct passed\n";
219 |
220 | vec.clear();
221 |
222 |
223 | for (int i = 0; i != 6000; ++i)
224 | {
225 | vec.push_back(rand() & 65535);
226 | }
227 |
228 | plf::indiesort(vec, std::greater());
229 |
230 | last = 65535;
231 |
232 | for (std::vector::iterator it = vec.begin(); it != vec.end(); ++it)
233 | {
234 | if (last < *it)
235 | {
236 | breakfail("Random small_struct last < *it!.");
237 | }
238 |
239 | last = *it;
240 | }
241 |
242 | std::cout << "Random Small Struct passed\n";
243 | }
244 |
245 |
246 |
247 | {
248 | title2("Test Basics C array");
249 |
250 | large_struct *structs = new large_struct[500];
251 |
252 | for (int i = 0; i != 500; ++i)
253 | {
254 | structs[i] = rand() & 65535;
255 | }
256 |
257 | plf::indiesort(&structs[0], &structs[500]);
258 |
259 | large_struct last = 0;
260 |
261 | for (int i = 0; i != 500; ++i)
262 | {
263 | if (last > structs[i])
264 | {
265 | breakfail("Random large_struct last > *it!.");
266 | }
267 |
268 | last = structs[i];
269 | }
270 |
271 | std::cout << "Random large_struct C array passed\n";
272 | }
273 |
274 |
275 |
276 | {
277 | title2("Test Basics small_struct_non_trivial");
278 |
279 | std::vector vec;
280 |
281 | for (int i = 60; i != -1; --i)
282 | {
283 | vec.push_back(i);
284 | }
285 |
286 | for (int i = 0; i != 60; ++i)
287 | {
288 | vec.push_back(i);
289 | }
290 |
291 | plf::indiesort(vec);
292 |
293 | int last = 0;
294 |
295 | for (std::vector::iterator it = vec.begin(); it != vec.end(); ++it)
296 | {
297 | if (last > **it)
298 | {
299 | breakfail("Sequential small_struct_non_trivial last > *it!.");
300 | }
301 |
302 | last = **it;
303 | }
304 |
305 | std::cout << "Sequential small_struct_non_trivial passed\n";
306 |
307 | vec.clear();
308 |
309 |
310 | for (int i = 0; i != 6000; ++i)
311 | {
312 | vec.push_back(rand() & 65535);
313 | }
314 |
315 | plf::indiesort(vec, std::greater());
316 |
317 | last = 65535;
318 |
319 | for (std::vector::iterator it = vec.begin(); it != vec.end(); ++it)
320 | {
321 | if (last < **it)
322 | {
323 | breakfail("Random small_struct_non_trivial last < *it!.");
324 | }
325 |
326 | last = **it;
327 | }
328 |
329 | std::cout << "Random small_struct_non_trivial passed\n";
330 | }
331 |
332 |
333 |
334 | {
335 | title2("Test Basics large_struct");
336 |
337 | std::vector vec;
338 |
339 | for (int i = 60; i != -1; --i)
340 | {
341 | vec.push_back(i);
342 | }
343 |
344 | for (int i = 0; i != 60; ++i)
345 | {
346 | vec.push_back(i);
347 | }
348 |
349 | plf::indiesort(vec);
350 |
351 | large_struct last = 0;
352 |
353 | for (std::vector::iterator it = vec.begin(); it != vec.end(); ++it)
354 | {
355 | if (last > **it)
356 | {
357 | breakfail("Sequential large_struct last > *it!.");
358 | }
359 |
360 | last = **it;
361 | }
362 |
363 | std::cout << "Sequential large_struct passed\n";
364 |
365 | vec.clear();
366 |
367 |
368 | for (int i = 0; i != 6000; ++i)
369 | {
370 | vec.push_back(rand() & 65535);
371 | }
372 |
373 | plf::indiesort(vec, std::greater());
374 |
375 | last = 65535;
376 |
377 | for (std::vector::iterator it = vec.begin(); it != vec.end(); ++it)
378 | {
379 | if (last < *it)
380 | {
381 | breakfail("Random large_struct last < *it!.");
382 | }
383 |
384 | last = *it;
385 | }
386 |
387 | std::cout << "Random large_struct passed\n";
388 | }
389 |
390 |
391 |
392 |
393 | {
394 | title2("std::list int range tests");
395 |
396 | {
397 | std::list ilist;
398 |
399 | for (int i = 0; i != 2000; ++i)
400 | {
401 | ilist.push_back(rand() & 65535);
402 | }
403 |
404 |
405 | std::list::iterator start = ilist.begin(), end = ilist.end();
406 | plf::indiesort(start, end);
407 |
408 |
409 | int last = 0;
410 |
411 | do
412 | {
413 | if (last > *start)
414 | {
415 | breakfail("std::list sort test > *it!.");
416 | }
417 |
418 | last = *start;
419 | } while (++start != end);
420 |
421 | std::cout << "std::list sort test passed\n";
422 | }
423 |
424 |
425 |
426 | {
427 | title2("std::list small_struct range tests");
428 |
429 | std::list sslist;
430 |
431 | for (int i = 0; i != 5000; ++i)
432 | {
433 | sslist.push_back(rand() & 65535);
434 | }
435 |
436 |
437 | std::list::iterator start = sslist.begin(), end = sslist.end();
438 | std::advance(start, 150);
439 | std::advance(end, -300);
440 |
441 | plf::indiesort(start, end);
442 |
443 |
444 | int last = 0;
445 |
446 | do
447 | {
448 | if (last > **start)
449 | {
450 | breakfail("std::list small_struct sort test > *it!.");
451 | }
452 |
453 | last = **start;
454 | } while (++start != end);
455 |
456 | std::cout << "std::list small_struct sort test passed\n";
457 | }
458 |
459 |
460 |
461 | {
462 | title2("std::list large_struct range tests");
463 |
464 | std::list lslist;
465 |
466 | for (int i = 0; i != 6000; ++i)
467 | {
468 | lslist.push_back(rand() & 65535);
469 | }
470 |
471 |
472 | plf::indiesort(lslist);
473 |
474 | int last = 0;
475 |
476 | std::list::iterator start = lslist.begin(), end = lslist.end();
477 |
478 | do
479 | {
480 | if (last > **start)
481 | {
482 | breakfail("std::list large_struct sort test > *it!.");
483 | }
484 |
485 | last = **start;
486 | } while (++start != end);
487 |
488 | std::cout << "std::list large_struct sort test passed\n";
489 | }
490 |
491 |
492 | }
493 | }
494 |
495 |
496 | title1("All tests passed! Press Enter to Exit.");
497 | std::cin.get();
498 |
499 | return 0;
500 | }
501 |
--------------------------------------------------------------------------------