├── LICENSE ├── Makefile ├── README ├── galgorithm.h ├── galgorithm.hpp ├── gheap.h ├── gheap.hpp ├── gheap_cpp03.hpp ├── gheap_cpp11.hpp ├── gpriority_queue.h ├── gpriority_queue.hpp ├── ops_count_test.cpp ├── perftests.c ├── perftests.cpp ├── tests.c └── tests.cpp /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 Aliaksandr Valialkin 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions 6 | are met: 7 | 1. Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 17 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 | SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CPP_COMPILER=g++ 2 | C_COMPILER=gcc 3 | 4 | COMMON_CFLAGS=-Wall -Wextra -Werror -pedantic 5 | 6 | DEBUG_CFLAGS=-g 7 | OPT_CFLAGS=-DNDEBUG -O2 8 | 9 | C_CFLAGS=$(COMMON_CFLAGS) -std=c99 10 | CPP03_CFLAGS=$(COMMON_CFLAGS) -std=c++98 11 | CPP11_CFLAGS=$(COMMON_CFLAGS) -std=c++0x -DGHEAP_CPP11 12 | 13 | all: tests perftests ops_count_test 14 | 15 | build-tests: 16 | $(C_COMPILER) tests.c $(C_CFLAGS) $(DEBUG_CFLAGS) -o tests_c 17 | $(CPP_COMPILER) tests.cpp $(CPP03_CFLAGS) $(DEBUG_CFLAGS) -o tests_cpp03 18 | $(CPP_COMPILER) tests.cpp $(CPP11_CFLAGS) $(DEBUG_CFLAGS) -o tests_cpp11 19 | 20 | tests: build-tests 21 | ./tests_c 22 | ./tests_cpp03 23 | ./tests_cpp11 24 | 25 | build-perftests: 26 | $(C_COMPILER) perftests.c $(C_CFLAGS) $(OPT_CFLAGS) -o perftests_c 27 | $(CPP_COMPILER) perftests.cpp $(CPP03_CFLAGS) $(OPT_CFLAGS) -o perftests_cpp03 28 | $(CPP_COMPILER) perftests.cpp $(CPP11_CFLAGS) $(OPT_CFLAGS) -o perftests_cpp11 29 | 30 | perftests: 31 | ./perftests_c 32 | ./perftests_cpp03 33 | ./perftests_cpp11 34 | 35 | build-ops_count_test: 36 | $(CPP_COMPILER) ops_count_test.cpp $(CPP03_CFLAGS) $(OPT_CFLAGS) -o ops_count_test_cpp03 37 | $(CPP_COMPILER) ops_count_test.cpp $(CPP11_CFLAGS) $(OPT_CFLAGS) -o ops_count_test_cpp11 38 | 39 | ops_count_test: 40 | ./ops_count_test_cpp03 41 | ./ops_count_test_cpp11 42 | 43 | clean: 44 | rm -f ./tests_c 45 | rm -f ./tests_cpp03 46 | rm -f ./tests_cpp11 47 | rm -f ./perftests_c 48 | rm -f ./perftests_cpp03 49 | rm -f ./perftests_cpp11 50 | rm -f ./ops_count_test_cpp03 51 | rm -f ./ops_count_test_cpp11 52 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Generalized heap implementation 2 | 3 | Generalized heap is based on usual heap data structure - 4 | http://en.wikipedia.org/wiki/Heap_%28data_structure%29 . 5 | 6 | It provides two additional paremeters, which allow optimizing heap 7 | for particular cases: 8 | 9 | * Fanout. The number of children per each heap node. 10 | * Fanout=1 corresponds to sorted List data structure. 11 | See http://en.wikipedia.org/wiki/List_%28computing%29 . 12 | * Fanout=2 corresponds to Binary heap. 13 | See http://en.wikipedia.org/wiki/Binary_heap . 14 | * Fanout>2 corresponds to D-heap. See http://en.wikipedia.org/wiki/D-heap . 15 | D-heap can be faster than Binary heap in the following cases: 16 | * If item comparison is faster than item assignment. 17 | * If sequential access to items is faster than non-sequential access 18 | to items. 19 | * If the number of 'decrease key' operations is larger than the number 20 | of 'pop heap' operations for min-heap. This is usually the case 21 | for Dijkstra algorithm 22 | ( http://en.wikipedia.org/wiki/Dijkstra%27s_algorithm ). 23 | 24 | * PageChunks. The number of chunks per each heap page. Each chunk contains 25 | Fanout items, so each heap page contains (PageChunks * Fanout) items. 26 | Items inside heap page are organized into a sub-heap with a root item outside 27 | the page. Leaf items in the page can be roots pointing to another pages. 28 | * PageChunks=1 corresponds to standard heap. 29 | * PageChunks>1 corresponds to B-heap. See http://en.wikipedia.org/wiki/B-heap. 30 | Heap pages containing more than one page chunk can be useful if multiple 31 | item accesses inside heap page is faster than multiple accesses to items 32 | across distinct heap pages. This can be the case for systems with virtual 33 | memory, where VM pages can be swapped out to slow media. 34 | Heap pages can be mapped to VM pages if PageChunks is calculated using 35 | the following formula: 36 | * PageChunks = sizeof(VM_page) / (sizeof(item) * Fanout) 37 | Perfrect alginment between VM pages and heap pages can be achieved if 38 | heap's root item is placed at the end of VM page. In this case the first 39 | child of the heap's root (i.e. the item with index 1) sits at the beginning 40 | of the next VM page. 41 | 42 | See also https://github.com/valyala/gheap/tree/sophisticated-gheap branch, 43 | which contains sophisticated gheap implementation with more complex heap layout 44 | and low-level optimizations. 45 | 46 | 47 | =============================================================================== 48 | The implementation provides the following functions: 49 | * Auxiliary functions: 50 | * get_parent_index() - returns parent index for the given child. 51 | * get_child_index() - returns the first child index for the given parent. 52 | * swap_max_item() - swaps heap's maximum item with the item outside heap 53 | and restores max heap invariant. 54 | * restore_heap_after_item_increase() - restores max heap invariant after 55 | the item's value increase. 56 | * restore_heap_after_item_decrease() - restores max heap invariant after 57 | the item's value decrease. 58 | * remove_from_heap() - removes the given item from the heap. 59 | 60 | * STL-like functions: 61 | * is_heap_until() - returns an iterator to the first non-heap item 62 | in the given range. 63 | * is_heap() - checks whether the given range contains valid heap. 64 | * make_heap() - creates a heap. 65 | * push_heap() - pushes the last element in the range to the heap. 66 | * pop_heap() - pops up the maximum element from the heap. 67 | * sort_heap() - sorts heap items in ascending order. 68 | 69 | * Heap-based algorithms: 70 | * heapsort() - performs heapsort. 71 | * partial_sort() - performs partial sort. 72 | * nway_merge() - performs N-way merge on top of the heap. 73 | * nway_mergesort() - performs N-way mergesort on top of the heap. 74 | 75 | The implementation is inspired by http://queue.acm.org/detail.cfm?id=1814327 , 76 | but it is more generalized. The implementation is optimized for speed. 77 | There are the following files: 78 | * gheap_cpp03.hpp - gheap optimized for C++03. 79 | * gheap_cpp11.hpp - gheap optimized for C++11. 80 | * gheap.hpp - switch file, which includes either gheap_cpp03.hpp 81 | or gheap_cpp11.hpp depending on whether GHEAP_CPP11 macro is defined. 82 | * gheap.h - gheap optimized for C99. 83 | * galgorithm.hpp - various algorithms on top of gheap for C++. 84 | * galgorithm.h - various algorithms on top of gheap for C99. 85 | * gpriority_queue.hpp - priority queue on top of gheap for C++. 86 | * gpriority_queue.h - priority queue on top of gheap for C99. 87 | 88 | Don't forget passing -DNDEBUG option to the compiler when creating optimized 89 | builds. This significantly speeds up gheap code by removing debug assertions. 90 | 91 | There are the following tests: 92 | * tests.cpp and tests.c - tests for gheap algorithms' correctness. 93 | * perftests.cpp and perftests.c - performance tests. 94 | * ops_count_test.cpp - the test, which counts the number of varius operations 95 | performed by gheap algorithms. 96 | 97 | =============================================================================== 98 | gheap for C++ usage 99 | 100 | #include "gheap.hpp" 101 | 102 | ... 103 | 104 | template 105 | void heapsort(vector &a) 106 | { 107 | Heap::make_heap(a.begin(), a.end()); 108 | Heap::sort_heap(a.begin(), a.end()); 109 | } 110 | 111 | typedef gheap<2, 1> binary_heap; 112 | heapsort(a); 113 | 114 | typedef gheap<4, 1> d4_heap; 115 | heapsort(a); 116 | 117 | typedef gheap<2, 512> paged_binary_heap; 118 | heapsort(a); 119 | 120 | 121 | =============================================================================== 122 | gheap for C usage 123 | 124 | #include "gheap.h" 125 | 126 | static void less(const void *const ctx, const void *const a, 127 | const void *const b) 128 | { 129 | (void)ctx; 130 | return *(int *)a < *(int *)b; 131 | } 132 | 133 | static void move(void *const dst, const void *const src) 134 | { 135 | *(int *)dst = *(int *)src; 136 | } 137 | 138 | static void heapsort(const struct gheap_ctx *const ctx, 139 | int *const a, const size_t n) 140 | { 141 | gheap_make_heap(ctx, a, n); 142 | gheap_sort_heap(ctx, a, n); 143 | } 144 | 145 | /* heapsort using binary heap */ 146 | static const struct gheap_ctx binary_heap_ctx = { 147 | .fanout = 2, 148 | .page_chunks = 1, 149 | .item_size = sizeof(int), 150 | .less_comparer = &less, 151 | .less_comparer_ctx = NULL, 152 | .item_mover = &move, 153 | }; 154 | heapsort(&binary_heap_ctx, a, n); 155 | 156 | /* heapsort using D-4 heap */ 157 | static const struct gheap_ctx d4_heap_ctx = { 158 | .fanout = 4, 159 | .page_chunks = 1, 160 | .item_size = sizeof(int), 161 | .less_comparer = &less, 162 | .less_comparer_ctx = NULL, 163 | .item_mover = &move, 164 | }; 165 | heapsort(&d4_heap_ctx, a, n); 166 | 167 | /* heapsort using paged binary heap */ 168 | static const struct gheap_ctx paged_binary_heap_ctx = { 169 | .fanout = 2, 170 | .page_chunks = 512, 171 | .item_size = sizeof(int), 172 | .less_comparer = &less, 173 | .less_comparer_ctx = NULL, 174 | .item_mover = &move, 175 | }; 176 | heapsort(&paged_binary_heap_ctx, a, n); 177 | -------------------------------------------------------------------------------- /galgorithm.h: -------------------------------------------------------------------------------- 1 | #ifndef GALGORITHM_H 2 | #define GALGORITHM_H 3 | 4 | /* 5 | * Generalized aglogithms based on gheap for C99. 6 | * 7 | * Don't forget passing -DNDEBUG option to the compiler when creating optimized 8 | * builds. This significantly speeds up gheap code by removing debug assertions. 9 | * 10 | * Author: Aliaksandr Valialkin . 11 | */ 12 | 13 | 14 | /******************************************************************************* 15 | * Interface. 16 | ******************************************************************************/ 17 | 18 | #include "gheap.h" /* for gheap_ctx */ 19 | 20 | #include /* for size_t */ 21 | 22 | /* 23 | * Sorts [base[0] ... base[n-1]] in ascending order via heapsort. 24 | */ 25 | static inline void galgorithm_heapsort(const struct gheap_ctx *const ctx, 26 | void *const base, const size_t n); 27 | 28 | /* 29 | * Performs partial sort, so [base[0] ... base[middle_index-1]) will contain 30 | * items sorted in ascending order, which are smaller than the rest of items 31 | * in the [base[middle_index] ... base[n-1]). 32 | */ 33 | static inline void galgorithm_partial_sort(const struct gheap_ctx *ctx, 34 | void *base, size_t n, size_t middle_index); 35 | 36 | /* 37 | * Vtable for input iterators, which is passed to galgorithm_nway_merge(). 38 | */ 39 | struct galgorithm_nway_merge_input_vtable 40 | { 41 | /* 42 | * Must advance the iterator to the next item. 43 | * Must return non-zero on success or 0 on the end of input. 44 | * 45 | * Galgorithm won't call this function after it returns 0. 46 | */ 47 | int (*next)(void *ctx); 48 | 49 | /* 50 | * Must return a pointer to the current item. 51 | * 52 | * Galgorithm won't call this function after the next() returns 0. 53 | */ 54 | const void *(*get)(const void *ctx); 55 | }; 56 | 57 | /* 58 | * A collection of input iterators, which is passed to galgorithm_nway_merge(). 59 | */ 60 | struct galgorithm_nway_merge_input 61 | { 62 | const struct galgorithm_nway_merge_input_vtable *vtable; 63 | 64 | /* 65 | * An array of opaque contexts, which are passed to vtable functions. 66 | * Each context represents a single input iterator. 67 | * Contextes must contain data reqired for fetching items from distinct 68 | * input iterators. 69 | * 70 | * Contextes in this array can be shuffled using ctx_mover. 71 | */ 72 | void *ctxs; 73 | 74 | /* The number of contextes. */ 75 | size_t ctxs_count; 76 | 77 | /* The size of each context object. */ 78 | size_t ctx_size; 79 | 80 | /* Is used for shuffling context objects. */ 81 | gheap_item_mover_t ctx_mover; 82 | }; 83 | 84 | /* 85 | * Vtable for output iterator, which is passed to galgorithm_nway_merge(). 86 | */ 87 | struct galgorithm_nway_merge_output_vtable 88 | { 89 | /* 90 | * Must put data into the output and advance the iterator 91 | * to the next position. 92 | */ 93 | void (*put)(void *ctx, const void *data); 94 | }; 95 | 96 | /* 97 | * Output iterator, which is passed to galgorithm_nway_merge(). 98 | */ 99 | struct galgorithm_nway_merge_output 100 | { 101 | const struct galgorithm_nway_merge_output_vtable *vtable; 102 | 103 | /* 104 | * An opaque context, which is passed to vtable functions. 105 | * The context must contain data essential for the output iterator. 106 | */ 107 | void *ctx; 108 | }; 109 | 110 | /* 111 | * Performs N-way merging of the given inputs into the output sorted 112 | * in ascending order, using ctx->less_comparer for items' comparison. 113 | * 114 | * Each input must hold non-zero number of items sorted in ascending order. 115 | * 116 | * As a side effect the function shuffles input contextes. 117 | */ 118 | static inline void galgorithm_nway_merge(const struct gheap_ctx *ctx, 119 | const struct galgorithm_nway_merge_input *input, 120 | const struct galgorithm_nway_merge_output *output); 121 | 122 | /* 123 | * Must sort the range [base[0] ... base[n-1]]. 124 | * ctx is small_range_sorter_ctx passed to galgorithm_nway_mergesort. 125 | */ 126 | typedef void (*galgorithm_nway_mergesort_small_range_sorter_t)( 127 | const void *ctx, void *base, size_t n); 128 | 129 | /* 130 | * Performs n-way mergesort for [base[0] ... base[range_size-1]] items. 131 | * 132 | * Uses small_range_sorter for sorting ranges containing no more 133 | * than small_range_size items. 134 | * 135 | * Splits the input range into subranges with small_range_size size, 136 | * sorts them using small_range_sorter and then merges them back 137 | * using n-way merge with n = subranges_count. 138 | * 139 | * items_tmp_buf must point to an uninitialized memory, which can hold 140 | * up to range_size items. 141 | */ 142 | static inline void galgorithm_nway_mergesort(const struct gheap_ctx *ctx, 143 | void *base, size_t range_size, 144 | galgorithm_nway_mergesort_small_range_sorter_t small_range_sorter, 145 | const void *small_range_sorter_ctx, 146 | size_t small_range_size, size_t subranges_count, void *items_tmp_buf); 147 | 148 | 149 | /******************************************************************************* 150 | * Implementation. 151 | * 152 | * Define all functions inline, so compiler will be able optimizing out common 153 | * args (fanout, page_chunks, item_size, less_comparer and item_mover), 154 | * which are usually constants, using constant folding optimization 155 | * ( http://en.wikipedia.org/wiki/Constant_folding ). 156 | *****************************************************************************/ 157 | 158 | #include "gheap.h" /* for gheap_* stuff */ 159 | 160 | #include /* for assert */ 161 | #include /* for size_t */ 162 | #include /* for uintptr_t, SIZE_MAX and UINTPTR_MAX */ 163 | #include /* for malloc(), free() */ 164 | 165 | /* Returns a pointer to base[index]. */ 166 | static inline void *_galgorithm_get_item_ptr( 167 | const struct gheap_ctx *const ctx, 168 | const void *const base, const size_t index) 169 | { 170 | const size_t item_size = ctx->item_size; 171 | 172 | assert(index <= SIZE_MAX / item_size); 173 | 174 | const size_t offset = item_size * index; 175 | assert((uintptr_t)base <= UINTPTR_MAX - offset); 176 | 177 | return ((char *)base) + offset; 178 | } 179 | 180 | /* Swaps items with given indexes */ 181 | static inline void _galgorithm_swap_items(const struct gheap_ctx *const ctx, 182 | const void *const base, const size_t a_index, const size_t b_index) 183 | { 184 | const size_t item_size = ctx->item_size; 185 | const gheap_item_mover_t item_mover = ctx->item_mover; 186 | 187 | char tmp[item_size]; 188 | void *const a = _galgorithm_get_item_ptr(ctx, base, a_index); 189 | void *const b = _galgorithm_get_item_ptr(ctx, base, b_index); 190 | item_mover(tmp, a); 191 | item_mover(a, b); 192 | item_mover(b, tmp); 193 | } 194 | 195 | static inline void galgorithm_heapsort(const struct gheap_ctx *const ctx, 196 | void *const base, const size_t n) 197 | { 198 | gheap_make_heap(ctx, base, n); 199 | gheap_sort_heap(ctx, base, n); 200 | } 201 | 202 | static inline void galgorithm_partial_sort(const struct gheap_ctx *const ctx, 203 | void *const base, const size_t n, const size_t middle_index) 204 | { 205 | assert(middle_index <= n); 206 | 207 | if (middle_index > 0) { 208 | gheap_make_heap(ctx, base, middle_index); 209 | 210 | const gheap_less_comparer_t less_comparer = ctx->less_comparer; 211 | const void *const less_comparer_ctx = ctx->less_comparer_ctx; 212 | 213 | for (size_t i = middle_index; i < n; ++i) { 214 | void *const tmp = _galgorithm_get_item_ptr(ctx, base, i); 215 | if (less_comparer(less_comparer_ctx, tmp, base)) { 216 | gheap_swap_max_item(ctx, base, middle_index, tmp); 217 | } 218 | } 219 | 220 | gheap_sort_heap(ctx, base, middle_index); 221 | } 222 | } 223 | 224 | struct _galgorithm_nway_merge_less_comparer_ctx 225 | { 226 | gheap_less_comparer_t less_comparer; 227 | const void *less_comparer_ctx; 228 | const struct galgorithm_nway_merge_input_vtable *vtable; 229 | }; 230 | 231 | static inline int _galgorithm_nway_merge_less_comparer(const void *const ctx, 232 | const void *const a, const void *const b) 233 | { 234 | const struct _galgorithm_nway_merge_less_comparer_ctx *const c = ctx; 235 | const gheap_less_comparer_t less_comparer = c->less_comparer; 236 | const void *const less_comparer_ctx = c->less_comparer_ctx; 237 | const struct galgorithm_nway_merge_input_vtable *const vtable = c->vtable; 238 | 239 | return less_comparer(less_comparer_ctx, vtable->get(b), vtable->get(a)); 240 | } 241 | 242 | static inline void galgorithm_nway_merge(const struct gheap_ctx *const ctx, 243 | const struct galgorithm_nway_merge_input *const input, 244 | const struct galgorithm_nway_merge_output *const output) 245 | { 246 | void *const top_input = input->ctxs; 247 | size_t inputs_count = input->ctxs_count; 248 | 249 | assert(inputs_count > 0); 250 | 251 | const struct _galgorithm_nway_merge_less_comparer_ctx less_comparer_ctx = { 252 | .less_comparer = ctx->less_comparer, 253 | .less_comparer_ctx = ctx->less_comparer_ctx, 254 | .vtable = input->vtable, 255 | }; 256 | const struct gheap_ctx nway_ctx = { 257 | .fanout = ctx->fanout, 258 | .page_chunks = ctx->page_chunks, 259 | .item_size = input->ctx_size, 260 | .less_comparer = &_galgorithm_nway_merge_less_comparer, 261 | .less_comparer_ctx = &less_comparer_ctx, 262 | .item_mover = input->ctx_mover, 263 | }; 264 | 265 | gheap_make_heap(&nway_ctx, top_input, inputs_count); 266 | while (1) { 267 | const void *const data = input->vtable->get(top_input); 268 | output->vtable->put(output->ctx, data); 269 | if (!input->vtable->next(top_input)) { 270 | --inputs_count; 271 | if (inputs_count == 0) { 272 | break; 273 | } 274 | _galgorithm_swap_items(&nway_ctx, top_input, 0, inputs_count); 275 | } 276 | gheap_restore_heap_after_item_decrease(&nway_ctx, top_input, 277 | inputs_count, 0); 278 | } 279 | } 280 | 281 | static inline void _galgorithm_move_items(const struct gheap_ctx *const ctx, 282 | void *const src, const size_t n, void *const dst) 283 | { 284 | const gheap_item_mover_t item_mover = ctx->item_mover; 285 | 286 | for (size_t i = 0; i < n; ++i) { 287 | item_mover( 288 | _galgorithm_get_item_ptr(ctx, dst, i), 289 | _galgorithm_get_item_ptr(ctx, src, i)); 290 | } 291 | } 292 | 293 | static inline void _galgorithm_sort_subranges(const struct gheap_ctx *const ctx, 294 | void *const base, const size_t range_size, 295 | const galgorithm_nway_mergesort_small_range_sorter_t small_range_sorter, 296 | const void *const small_range_sorter_ctx, 297 | const size_t small_range_size) 298 | { 299 | assert(small_range_size > 0); 300 | 301 | const size_t last_full_range = range_size - range_size % small_range_size; 302 | for (size_t i = 0; i != last_full_range; i += small_range_size) { 303 | small_range_sorter(small_range_sorter_ctx, 304 | _galgorithm_get_item_ptr(ctx, base, i), small_range_size); 305 | } 306 | 307 | /* Sort the last subrange, which contains less than small_range_size items. */ 308 | if (last_full_range < range_size) { 309 | small_range_sorter(small_range_sorter_ctx, 310 | _galgorithm_get_item_ptr(ctx, base, last_full_range), 311 | range_size - last_full_range); 312 | } 313 | } 314 | 315 | struct _galgorithm_nway_mergesort_input_ctx 316 | { 317 | const struct gheap_ctx *ctx; 318 | const void *next; 319 | const void *last; 320 | }; 321 | 322 | static inline int _galgorithm_nway_mergesort_input_next(void *ctx) 323 | { 324 | struct _galgorithm_nway_mergesort_input_ctx *const c = ctx; 325 | 326 | assert(c->next < c->last); 327 | c->next = _galgorithm_get_item_ptr(c->ctx, c->next, 1); 328 | assert(c->next <= c->last); 329 | return (c->next < c->last); 330 | } 331 | 332 | static inline const void *_galgorithm_nway_mergesort_input_get(const void *ctx) 333 | { 334 | const struct _galgorithm_nway_mergesort_input_ctx *const c = ctx; 335 | 336 | assert(c->next < c->last); 337 | return c->next; 338 | } 339 | 340 | static const struct galgorithm_nway_merge_input_vtable 341 | _galgorithm_nway_mergesort_input_vtable = { 342 | .next = &_galgorithm_nway_mergesort_input_next, 343 | .get = &_galgorithm_nway_mergesort_input_get, 344 | }; 345 | 346 | struct _galgorithm_nway_mergesort_output_ctx 347 | { 348 | const struct gheap_ctx *ctx; 349 | void *next; 350 | }; 351 | 352 | static inline void _galgorithm_nway_mergesort_output_put(void *ctx, 353 | const void *data) 354 | { 355 | struct _galgorithm_nway_mergesort_output_ctx *const c = ctx; 356 | const gheap_item_mover_t item_mover = c->ctx->item_mover; 357 | 358 | item_mover(c->next, data); 359 | c->next = _galgorithm_get_item_ptr(c->ctx, c->next, 1); 360 | } 361 | 362 | static const struct galgorithm_nway_merge_output_vtable 363 | _galgorithm_nway_mergesort_output_vtable = { 364 | .put = &_galgorithm_nway_mergesort_output_put, 365 | }; 366 | 367 | static inline void _galgorithm_merge_subrange_tuples( 368 | const struct gheap_ctx *const ctx, void *const base, const size_t range_size, 369 | struct galgorithm_nway_merge_input *const input, 370 | const struct galgorithm_nway_merge_output *const output, 371 | const size_t subranges_count, const size_t subrange_size) 372 | { 373 | assert(subranges_count > 1); 374 | assert(subrange_size > 0); 375 | 376 | struct _galgorithm_nway_mergesort_input_ctx *const input_ctxs = input->ctxs; 377 | input->ctxs_count = subranges_count; 378 | 379 | size_t i = 0; 380 | 381 | /* 382 | * Merge full subrange tuples. Each full subrange tuple contains 383 | * subranges_count full subranges. Each full subrange contains 384 | * subrange_size items. 385 | */ 386 | if (subrange_size <= range_size / subranges_count) { 387 | const size_t tuple_size = subrange_size * subranges_count; 388 | const size_t last_full_tuple = range_size - range_size % tuple_size; 389 | 390 | while (i != last_full_tuple) { 391 | for (size_t j = 0; j < subranges_count; ++j) { 392 | input_ctxs[j].next = _galgorithm_get_item_ptr(ctx, base, i); 393 | i += subrange_size; 394 | input_ctxs[j].last = _galgorithm_get_item_ptr(ctx, base, i); 395 | } 396 | 397 | galgorithm_nway_merge(ctx, input, output); 398 | } 399 | } 400 | 401 | /* 402 | * Merge tail subrange tuple. Tail subrange tuple contains less than 403 | * subranges_count full subranges. It also may contain tail subrange 404 | * with less than subrange_size items. 405 | */ 406 | const size_t tail_tuple_size = range_size - i; 407 | if (tail_tuple_size > 0) { 408 | const size_t full_subranges_count = tail_tuple_size / subrange_size; 409 | assert(full_subranges_count < subranges_count); 410 | size_t tail_subranges_count = full_subranges_count; 411 | 412 | for (size_t j = 0; j < full_subranges_count; ++j) { 413 | input_ctxs[j].next = _galgorithm_get_item_ptr(ctx, base, i); 414 | i += subrange_size; 415 | input_ctxs[j].last = _galgorithm_get_item_ptr(ctx, base, i); 416 | } 417 | 418 | if (i < range_size) { 419 | input_ctxs[full_subranges_count].next = 420 | _galgorithm_get_item_ptr(ctx, base, i); 421 | input_ctxs[full_subranges_count].last = 422 | _galgorithm_get_item_ptr(ctx, base, range_size); 423 | ++tail_subranges_count; 424 | } 425 | 426 | input->ctxs_count = tail_subranges_count; 427 | galgorithm_nway_merge(ctx, input, output); 428 | } 429 | } 430 | 431 | static inline void _galgorithm_nway_mergesort_input_ctx_mover(void *dst, 432 | const void *src) 433 | { 434 | *(struct _galgorithm_nway_mergesort_input_ctx *)dst = 435 | *(struct _galgorithm_nway_mergesort_input_ctx *)src; 436 | } 437 | 438 | static inline void galgorithm_nway_mergesort(const struct gheap_ctx *const ctx, 439 | void *const base, const size_t range_size, 440 | const galgorithm_nway_mergesort_small_range_sorter_t small_range_sorter, 441 | const void *const small_range_sorter_ctx, 442 | const size_t small_range_size, const size_t subranges_count, 443 | void *const items_tmp_buf) 444 | { 445 | assert(small_range_size > 0); 446 | assert(subranges_count > 1); 447 | 448 | /* Preparation: Move items to a temporary buffer. */ 449 | _galgorithm_move_items(ctx, base, range_size, items_tmp_buf); 450 | 451 | /* 452 | * Step 1: split the range into subranges with small_range_size size each 453 | * (except the last subrange, which may contain less than small_range_size 454 | * items) and sort each of these subranges using small_range_sorter. 455 | */ 456 | _galgorithm_sort_subranges(ctx, items_tmp_buf, range_size, 457 | small_range_sorter, small_range_sorter_ctx, small_range_size); 458 | 459 | /* Step 2: Merge subranges sorted at the previous step using n-way merge. */ 460 | struct _galgorithm_nway_mergesort_input_ctx *const input_ctxs = 461 | malloc(sizeof(input_ctxs[0]) * subranges_count); 462 | for (size_t i = 0; i < subranges_count; ++i) { 463 | input_ctxs[i].ctx = ctx; 464 | } 465 | 466 | struct galgorithm_nway_merge_input input = { 467 | .vtable = &_galgorithm_nway_mergesort_input_vtable, 468 | .ctxs = input_ctxs, 469 | .ctxs_count = subranges_count, 470 | .ctx_size = sizeof(input_ctxs[0]), 471 | .ctx_mover = &_galgorithm_nway_mergesort_input_ctx_mover, 472 | }; 473 | 474 | struct _galgorithm_nway_mergesort_output_ctx output_ctx; 475 | output_ctx.ctx = ctx; 476 | 477 | const struct galgorithm_nway_merge_output output = { 478 | .vtable = &_galgorithm_nway_mergesort_output_vtable, 479 | .ctx = &output_ctx, 480 | }; 481 | 482 | size_t subrange_size = small_range_size; 483 | for (;;) { 484 | /* 485 | * First pass: merge items from the temporary buffer 486 | * to the original location. 487 | */ 488 | output_ctx.next = base; 489 | _galgorithm_merge_subrange_tuples(ctx, items_tmp_buf, range_size, 490 | &input, &output, subranges_count, subrange_size); 491 | 492 | if (subrange_size > range_size / subranges_count) { 493 | break; 494 | } 495 | subrange_size *= subranges_count; 496 | 497 | /* 498 | * Second pass: merge items from the original location 499 | * to the temporary buffer. 500 | */ 501 | output_ctx.next = items_tmp_buf; 502 | _galgorithm_merge_subrange_tuples(ctx, base, range_size, 503 | &input, &output, subranges_count, subrange_size); 504 | 505 | if (subrange_size > range_size / subranges_count) { 506 | /* Move items from the temporary buffer to the original location. */ 507 | _galgorithm_move_items(ctx, items_tmp_buf, range_size, base); 508 | break; 509 | } 510 | subrange_size *= subranges_count; 511 | } 512 | 513 | free(input_ctxs); 514 | } 515 | 516 | 517 | 518 | #endif 519 | -------------------------------------------------------------------------------- /galgorithm.hpp: -------------------------------------------------------------------------------- 1 | #ifndef GALGORITHM_H 2 | #define GALGORITHM_H 3 | 4 | // Generalized algorithms based on Heap. 5 | // 6 | // Pass -DGHEAP_CPP11 to compiler for enabling C++11 optimization, 7 | // otherwise C++03 optimization will be enabled. 8 | // 9 | // Don't forget passing -DNDEBUG option to the compiler when creating optimized 10 | // builds. This significantly speeds up the code by removing debug assertions. 11 | // 12 | // Author: Aliaksandr Valialkin . 13 | 14 | #include "gheap.hpp" 15 | 16 | #include // for assert 17 | #include // for size_t, ptrdiff_t 18 | #include // for std::iterator_traits, std::advance() 19 | #include // for std::*_temporary_buffer() 20 | #include // for std::bad_alloc 21 | #include // for std::move(), std::swap(), std::*pair 22 | 23 | template > 24 | class galgorithm 25 | { 26 | private: 27 | 28 | // Standard less comparer. 29 | template 30 | static bool _std_less_comparer( 31 | const typename std::iterator_traits::value_type &a, 32 | const typename std::iterator_traits::value_type &b) 33 | { 34 | return (a < b); 35 | } 36 | 37 | // Less comparer for nway_merge(). 38 | template 39 | class _nway_merge_less_comparer 40 | { 41 | private: 42 | const LessComparer &_less_comparer; 43 | 44 | public: 45 | _nway_merge_less_comparer(const LessComparer &less_comparer) : 46 | _less_comparer(less_comparer) {} 47 | 48 | template 49 | bool operator() ( 50 | const std::pair &input_range_a, 51 | const std::pair &input_range_b) const 52 | { 53 | assert(input_range_a.first != input_range_a.second); 54 | assert(input_range_b.first != input_range_b.second); 55 | 56 | return _less_comparer(*(input_range_b.first), *(input_range_a.first)); 57 | } 58 | }; 59 | 60 | // RAII wrapper around temporary buffer. 61 | // It is used by nway_mergesort() for allocation of temporary memory. 62 | template 63 | class _temporary_buffer 64 | { 65 | private: 66 | T *_ptr; 67 | 68 | public: 69 | _temporary_buffer(const size_t size) 70 | { 71 | const std::pair tmp_buf = 72 | std::get_temporary_buffer(size); 73 | _ptr = tmp_buf.first; 74 | assert(tmp_buf.second >= 0); 75 | if (_ptr == 0 || (size_t)tmp_buf.second < size) { 76 | // It is OK passing (_ptr == 0) to std::return_temporary_buffer(). 77 | std::return_temporary_buffer(_ptr); 78 | throw std::bad_alloc(); 79 | } 80 | } 81 | 82 | ~_temporary_buffer() 83 | { 84 | std::return_temporary_buffer(_ptr); 85 | _ptr = 0; 86 | } 87 | 88 | T *get_ptr() const 89 | { 90 | return _ptr; 91 | } 92 | }; 93 | 94 | // Standard sorter for small ranges. 95 | template 96 | static void _std_small_range_sorter(T *const first, T *const last, 97 | const LessComparer &less_comparer) 98 | { 99 | assert(first <= last); 100 | 101 | // Insertion sort implementation. 102 | // See http://en.wikipedia.org/wiki/Insertion_sort . 103 | 104 | for (T *it = first + 1; it != last; ++it) { 105 | #ifdef GHEAP_CPP11 106 | T tmp = std::move(*it); 107 | T *hole = it; 108 | while (hole != first && less_comparer(tmp, *(hole - 1))) { 109 | *hole = std::move(*(hole - 1)); 110 | --hole; 111 | } 112 | *hole = std::move(tmp); 113 | #else 114 | T *hole = it; 115 | while (hole != first && less_comparer(*hole, *(hole - 1))) { 116 | std::swap(*hole, *(hole - 1)); 117 | --hole; 118 | } 119 | #endif 120 | } 121 | } 122 | 123 | // Moves items from [first ... last) to uninitialized memory pointed 124 | // by result. 125 | template 126 | static ForwardIterator _uninitialized_move_items(const InputIterator &first, 127 | const InputIterator &last, const ForwardIterator &result) 128 | { 129 | #ifdef GHEAP_CPP11 130 | // libstdc++ is missing std::uninitialized_move(), so wrap 131 | // the input iterator into std::make_move_iterator(). 132 | // See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=51981 . 133 | return std::uninitialized_copy(std::make_move_iterator(first), 134 | std::make_move_iterator(last), result); 135 | #else 136 | return std::uninitialized_copy(first, last, result); 137 | #endif 138 | } 139 | 140 | // Moves items from [first ... last) to result. 141 | template 142 | static OutputIterator _move_items(const InputIterator &first, 143 | const InputIterator &last, const OutputIterator &result) 144 | { 145 | #ifdef GHEAP_CPP11 146 | return std::move(first, last, result); 147 | #else 148 | return std::copy(first, last, result); 149 | #endif 150 | } 151 | 152 | // Auxiliary function for nway_mergesort(). 153 | // Splits the range [first ... last) into subranges with small_range_size size 154 | // each (except the last subrange, which may contain less 155 | // than small_range_size items) and sort each of these subranges using 156 | // small_range_sorter. 157 | template 159 | static void _sort_subranges(const RandomAccessIterator &first, 160 | const RandomAccessIterator &last, const LessComparer &less_comparer, 161 | const SmallRangeSorter &small_range_sorter, 162 | const size_t small_range_size) 163 | { 164 | assert(first <= last); 165 | assert(small_range_size > 0); 166 | 167 | const size_t range_size = last - first; 168 | 169 | const RandomAccessIterator it_last = last - range_size % small_range_size; 170 | RandomAccessIterator it = first; 171 | while (it != it_last) { 172 | const RandomAccessIterator it_first = it; 173 | it += small_range_size; 174 | small_range_sorter(it_first, it, less_comparer); 175 | } 176 | 177 | // Sort the last subrange, which contains less than small_range_size items. 178 | if (it < last) { 179 | small_range_sorter(it, last, less_comparer); 180 | } 181 | } 182 | 183 | // Auxiliary function for nway_mergesort(). 184 | // Merges subranges inside each subrange tuple. 185 | // Each subrange tuple contains subranges_count subranges, except the last 186 | // tuple, which may contain less than subranges_count subranges. 187 | // Each subrange contains subrange_size items, except the last subrange, 188 | // which may contain less than subrange_size items. 189 | template 190 | static void _merge_subrange_tuples(const InputIterator &first, 191 | const InputIterator &last, const OutputIterator &result, 192 | const LessComparer &less_comparer, 193 | std::pair *const subranges, 194 | const size_t subranges_count, const size_t subrange_size) 195 | { 196 | assert(first <= last); 197 | assert(subranges_count > 1); 198 | assert(subrange_size > 0); 199 | 200 | typedef std::pair subrange_t; 201 | 202 | const size_t range_size = last - first; 203 | InputIterator it = first; 204 | OutputIterator output = result; 205 | 206 | // Merge full subrange tuples. Each full subrange tuple contains 207 | // subranges_count full subranges. Each full subrange contains 208 | // subrange_size items. 209 | if (subrange_size <= range_size / subranges_count) { 210 | const size_t tuple_size = subrange_size * subranges_count; 211 | const InputIterator it_last = last - range_size % tuple_size; 212 | 213 | while (it != it_last) { 214 | for (size_t i = 0; i < subranges_count; ++i) { 215 | const InputIterator it_first = it; 216 | std::advance(it, subrange_size); 217 | new (subranges + i) subrange_t(it_first, it); 218 | } 219 | 220 | output = nway_merge(subranges, subranges + subranges_count, output, 221 | less_comparer); 222 | 223 | for (size_t i = 0; i < subranges_count; ++i) { 224 | subranges[i].~subrange_t(); 225 | } 226 | } 227 | } 228 | 229 | // Merge tail subrange tuple. Tail subrange tuple contains less than 230 | // subranges_count full subranges. It also may contain tail subrange 231 | // with less than subrange_size items. 232 | const size_t tail_tuple_size = last - it; 233 | if (tail_tuple_size > 0) { 234 | const size_t full_subranges_count = tail_tuple_size / subrange_size; 235 | assert(full_subranges_count < subranges_count); 236 | size_t tail_subranges_count = full_subranges_count; 237 | 238 | for (size_t i = 0; i < full_subranges_count; ++i) { 239 | const InputIterator it_first = it; 240 | std::advance(it, subrange_size); 241 | new (subranges + i) subrange_t(it_first, it); 242 | } 243 | 244 | if (it < last) { 245 | new (subranges + full_subranges_count) subrange_t(it, last); 246 | ++tail_subranges_count; 247 | } 248 | 249 | nway_merge(subranges, subranges + tail_subranges_count, output, 250 | less_comparer); 251 | 252 | for (size_t i = 0; i < tail_subranges_count; ++i) { 253 | subranges[i].~subrange_t(); 254 | } 255 | } 256 | } 257 | 258 | public: 259 | 260 | // Sorts items [first ... middle) in ascending order. 261 | // Uses less_comparer for items' comparison. 262 | // 263 | // std::swap() specialization and/or move constructor/assignment 264 | // may be provided for non-trivial items as a speed optimization. 265 | template 266 | static void heapsort(const RandomAccessIterator &first, 267 | const RandomAccessIterator &last, const LessComparer &less_comparer) 268 | { 269 | Heap::make_heap(first, last, less_comparer); 270 | Heap::sort_heap(first, last, less_comparer); 271 | } 272 | 273 | // Sorts items [first ... middle) in ascending order. 274 | // Uses operator< for items' comparison. 275 | // 276 | // std::swap() specialization and/or move constructor/assignment 277 | // may be provided for non-trivial items as a speed optimization. 278 | template 279 | static void heapsort(const RandomAccessIterator &first, 280 | const RandomAccessIterator &last) 281 | { 282 | heapsort(first, last, _std_less_comparer); 283 | } 284 | 285 | // Performs partial sort, so [first ... middle) will contain items sorted 286 | // in ascending order, which are smaller than the rest of items 287 | // in the [middle ... last). 288 | // Uses less_comparer for items' comparison. 289 | // 290 | // std::swap() specialization and/or move constructor/assignment 291 | // may be provided for non-trivial items as a speed optimization. 292 | template 293 | static void partial_sort(const RandomAccessIterator &first, 294 | const RandomAccessIterator &middle, const RandomAccessIterator &last, 295 | const LessComparer &less_comparer) 296 | { 297 | assert(first <= middle); 298 | assert(middle <= last); 299 | 300 | typedef typename std::iterator_traits::value_type 301 | value_type; 302 | 303 | const size_t sorted_range_size = middle - first; 304 | if (sorted_range_size > 0) { 305 | Heap::make_heap(first, middle, less_comparer); 306 | 307 | const size_t heap_size = last - first; 308 | for (size_t i = sorted_range_size; i < heap_size; ++i) { 309 | if (less_comparer(first[i], first[0])) { 310 | Heap::swap_max_item(first, middle, first[i], less_comparer); 311 | } 312 | } 313 | 314 | Heap::sort_heap(first, middle, less_comparer); 315 | } 316 | } 317 | 318 | // Performs partial sort, so [first ... middle) will contain items sorted 319 | // in ascending order, which are smaller than the rest of items 320 | // in the [middle ... last). 321 | // Uses operator< for items' comparison. 322 | // 323 | // std::swap() specialization and/or move constructor/assignment 324 | // may be provided for non-trivial items as a speed optimization. 325 | template 326 | static void partial_sort(const RandomAccessIterator &first, 327 | const RandomAccessIterator &middle, const RandomAccessIterator &last) 328 | { 329 | partial_sort(first, middle, last, _std_less_comparer); 330 | } 331 | 332 | // Performs N-way merging of the given input ranges into the result sorted 333 | // in ascending order, using less_comparer for items' comparison. 334 | // 335 | // Each input range must hold non-zero number of items sorted 336 | // in ascending order. Each range is defined as a std::pair containing 337 | // input iterators, where the first iterator points to the beginning 338 | // of the range, while the second iterator points to the end of the range. 339 | // 340 | // Returns an iterator pointing to the next element in the result after 341 | // the merge. 342 | // 343 | // std::swap() specialization and/or move constructor/assignment 344 | // may be provided for non-trivial input ranges as a speed optimization. 345 | // 346 | // As a side effect the function shuffles input ranges between 347 | // [input_ranges_first ... input_ranges_last) and sets the first iterator 348 | // for each input range to the end of the corresponding range. 349 | // 350 | // Also values from input ranges may become obsolete after 351 | // the funtion return, because they can be moved to the result via 352 | // move construction or move assignment in C++11. 353 | template 355 | static OutputIterator nway_merge( 356 | const RandomAccessIterator &input_ranges_first, 357 | const RandomAccessIterator &input_ranges_last, 358 | const OutputIterator &result, const LessComparer &less_comparer) 359 | { 360 | assert(input_ranges_first < input_ranges_last); 361 | 362 | typedef typename std::iterator_traits::value_type 363 | input_range_iterator; 364 | 365 | const RandomAccessIterator &first = input_ranges_first; 366 | RandomAccessIterator last = input_ranges_last; 367 | OutputIterator output = result; 368 | 369 | const _nway_merge_less_comparer less(less_comparer); 370 | 371 | Heap::make_heap(first, last, less); 372 | while (true) { 373 | input_range_iterator &input_range = first[0]; 374 | assert(input_range.first != input_range.second); 375 | #ifdef GHEAP_CPP11 376 | *output = std::move(*(input_range.first)); 377 | #else 378 | *output = *(input_range.first); 379 | #endif 380 | ++output; 381 | ++(input_range.first); 382 | if (input_range.first == input_range.second) { 383 | --last; 384 | if (first == last) { 385 | break; 386 | } 387 | std::swap(input_range, *last); 388 | } 389 | Heap::restore_heap_after_item_decrease(first, first, last, less); 390 | } 391 | 392 | return output; 393 | } 394 | 395 | // Performs N-way merging of the given input ranges into the result sorted 396 | // in ascending order, using operator< for items' comparison. 397 | // 398 | // Each input range must hold non-zero number of items sorted 399 | // in ascending order. Each range is defined as a std::pair containing 400 | // input iterators, where the first iterator points to the beginning 401 | // of the range, while the second iterator points to the end of the range. 402 | // 403 | // Returns an iterator pointing to the next element in the result after 404 | // the merge. 405 | // 406 | // std::swap() specialization and/or move constructor/assignment 407 | // may be provided for non-trivial input ranges as a speed optimization. 408 | // 409 | // As a side effect the function shuffles input ranges between 410 | // [input_ranges_first ... input_ranges_last) and sets the first iterator 411 | // for each input range to the end of the corresponding range. 412 | // 413 | // Also values from input ranges may become obsolete after 414 | // the function return, because they can be moved to the result via 415 | // move construction or move assignment in C++11. 416 | template 417 | static OutputIterator nway_merge( 418 | const RandomAccessIterator &input_ranges_first, 419 | const RandomAccessIterator &input_ranges_last, 420 | const OutputIterator &result) 421 | { 422 | typedef typename std::iterator_traits::value_type::first_type input_iterator; 424 | 425 | return nway_merge(input_ranges_first, input_ranges_last, result, 426 | _std_less_comparer); 427 | } 428 | 429 | // Performs n-way mergesort. 430 | // 431 | // Uses: 432 | // - less_comparer for items' comparison. 433 | // - small_range_sorter for sorting ranges containing no more 434 | // than small_range_size items. 435 | // 436 | // Splits the input range into subranges with small_range_size size, 437 | // sorts them using small_range_sorter and then merges them back 438 | // using n-way merge with n = subranges_count. 439 | // 440 | // items_tmp_buf must point to an uninitialized memory, which can hold 441 | // up to (last - first) items. 442 | // 443 | // May raise std::bad_alloc on unsuccessful attempt to allocate temporary 444 | // space for auxiliary structures required for n-way merging. 445 | template 446 | static void nway_mergesort(const ForwardIterator &first, 447 | const ForwardIterator &last, const LessComparer &less_comparer, 448 | const SmallRangeSorter &small_range_sorter, 449 | const size_t small_range_size, const size_t subranges_count, 450 | typename std::iterator_traits::value_type 451 | *const items_tmp_buf) 452 | { 453 | assert(first <= last); 454 | assert(small_range_size > 0); 455 | assert(subranges_count > 1); 456 | 457 | typedef typename std::iterator_traits::value_type 458 | value_type; 459 | typedef std::pair subrange1_t; 460 | typedef std::pair subrange2_t; 461 | 462 | const size_t range_size = last - first; 463 | 464 | // Preparation: Move items to a temporary buffer. 465 | _uninitialized_move_items(first, last, items_tmp_buf); 466 | 467 | // Step 1: split the range into subranges with small_range_size size each 468 | // (except the last subrange, which may contain less than small_range_size 469 | // items) and sort each of these subranges using small_range_sorter. 470 | _sort_subranges(items_tmp_buf, items_tmp_buf + range_size, 471 | less_comparer, small_range_sorter, small_range_size); 472 | 473 | // Step 2: Merge subranges sorted at the previous step using n-way merge. 474 | const _temporary_buffer subranges_tmp_buf1(subranges_count); 475 | const _temporary_buffer subranges_tmp_buf2(subranges_count); 476 | 477 | size_t subrange_size = small_range_size; 478 | for (;;) { 479 | // First pass: merge items from the temporary buffer 480 | // to the original location. 481 | _merge_subrange_tuples( 482 | items_tmp_buf, items_tmp_buf + range_size, first, less_comparer, 483 | subranges_tmp_buf2.get_ptr(), subranges_count, subrange_size); 484 | 485 | if (subrange_size > range_size / subranges_count) { 486 | break; 487 | } 488 | subrange_size *= subranges_count; 489 | 490 | // Second pass: merge items from the original location 491 | // to the temporary buffer. 492 | _merge_subrange_tuples( 493 | first, last, items_tmp_buf, less_comparer, 494 | subranges_tmp_buf1.get_ptr(), subranges_count, subrange_size); 495 | 496 | if (subrange_size > range_size / subranges_count) { 497 | // Move items from the temporary buffer to the original location. 498 | _move_items(items_tmp_buf, items_tmp_buf + range_size, first); 499 | break; 500 | } 501 | subrange_size *= subranges_count; 502 | } 503 | 504 | // Destroy dummy items in the temporary buffer. 505 | for (size_t i = 0; i < range_size; ++i) { 506 | items_tmp_buf[i].~value_type(); 507 | } 508 | } 509 | 510 | // Performs n-way mergesort. 511 | // 512 | // Uses: 513 | // - less_comparer for items' comparison. 514 | // - small_range_sorter for sorting ranges containing no more 515 | // than small_range_size items. 516 | // 517 | // Splits the input range into subranges with small_range_size size, 518 | // sorts them using small_range_sorter and then merges them back 519 | // using n-way merge with n = subranges_count. 520 | // 521 | // May raise std::bad_alloc on unsuccessful attempt to allocate a temporary 522 | // buffer for (last - first) items. 523 | template 524 | static void nway_mergesort(const ForwardIterator &first, 525 | const ForwardIterator &last, const LessComparer &less_comparer, 526 | const SmallRangeSorter &small_range_sorter, 527 | const size_t small_range_size = 32, const size_t subranges_count = 15) 528 | { 529 | assert(first <= last); 530 | 531 | typedef typename std::iterator_traits::value_type 532 | value_type; 533 | 534 | const size_t range_size = last - first; 535 | 536 | const _temporary_buffer tmp_buf(range_size); 537 | value_type *const items_tmp_buf = tmp_buf.get_ptr(); 538 | 539 | nway_mergesort(first, last, less_comparer, small_range_sorter, 540 | small_range_size, subranges_count, items_tmp_buf); 541 | } 542 | 543 | // Performs n-way mergesort. 544 | // 545 | // Uses less_comparer for items' comparison. 546 | // 547 | // May raise std::bad_alloc on unsuccessful attempt to allocate a temporary 548 | // buffer for (last - first) items. 549 | template 550 | static void nway_mergesort(const ForwardIterator &first, 551 | const ForwardIterator &last, const LessComparer &less_comparer) 552 | { 553 | typedef typename std::iterator_traits::value_type 554 | value_type; 555 | 556 | nway_mergesort(first, last, less_comparer, 557 | _std_small_range_sorter); 558 | } 559 | 560 | // Performs n-way mergesort. 561 | // 562 | // Uses operator< for items' comparison. 563 | // 564 | // May raise std::bad_alloc on unsuccessful attempt to allocate a temporary 565 | // buffer for (last - first) items. 566 | template 567 | static void nway_mergesort(const ForwardIterator &first, 568 | const ForwardIterator &last) 569 | { 570 | nway_mergesort(first, last, _std_less_comparer); 571 | } 572 | }; 573 | #endif 574 | -------------------------------------------------------------------------------- /gheap.h: -------------------------------------------------------------------------------- 1 | #ifndef GHEAP_H 2 | #define GHEAP_H 3 | 4 | /* 5 | * Generalized heap implementation for C99. 6 | * 7 | * Don't forget passing -DNDEBUG option to the compiler when creating optimized 8 | * builds. This significantly speeds up gheap code by removing debug assertions. 9 | * 10 | * Author: Aliaksandr Valialkin . 11 | */ 12 | 13 | 14 | /******************************************************************************* 15 | * Interface. 16 | ******************************************************************************/ 17 | 18 | #include /* for size_t */ 19 | #include /* for SIZE_MAX */ 20 | 21 | /* 22 | * Less comparer must return non-zero value if a < b. 23 | * ctx is the gheap_ctx->less_comparer_ctx. 24 | * Otherwise it must return 0. 25 | */ 26 | typedef int (*gheap_less_comparer_t)(const void *ctx, const void *a, 27 | const void *b); 28 | 29 | /* 30 | * Moves the item from src to dst. 31 | */ 32 | typedef void (*gheap_item_mover_t)(void *dst, const void *src); 33 | 34 | /* 35 | * Gheap context. 36 | * This context must be passed to every gheap function. 37 | */ 38 | struct gheap_ctx 39 | { 40 | /* 41 | * How much children each heap item can have. 42 | */ 43 | size_t fanout; 44 | 45 | /* 46 | * A chunk is a tuple containing fanout items arranged sequentially in memory. 47 | * A page is a subheap containing page_chunks chunks arranged sequentially 48 | * in memory. 49 | * The number of chunks in a page is an arbitrary integer greater than 0. 50 | */ 51 | size_t page_chunks; 52 | 53 | /* 54 | * The size of each item in bytes. 55 | */ 56 | size_t item_size; 57 | 58 | gheap_less_comparer_t less_comparer; 59 | const void *less_comparer_ctx; 60 | 61 | gheap_item_mover_t item_mover; 62 | }; 63 | 64 | /* 65 | * Returns parent index for the given child index. 66 | * Child index must be greater than 0. 67 | * Returns 0 if the parent is root. 68 | */ 69 | static inline size_t gheap_get_parent_index(const struct gheap_ctx *ctx, 70 | size_t u); 71 | 72 | /* 73 | * Returns the index of the first child for the given parent index. 74 | * Parent index must be less than SIZE_MAX. 75 | * Returns SIZE_MAX if the index of the first child for the given parent 76 | * cannot fit size_t. 77 | */ 78 | static inline size_t gheap_get_child_index(const struct gheap_ctx *ctx, 79 | size_t u); 80 | 81 | /* 82 | * Returns a pointer to the first non-heap item using less_comparer 83 | * for items' comparison. 84 | * Returns the index of the first non-heap item. 85 | * Returns heap_size if base points to valid max heap with the given size. 86 | */ 87 | static inline size_t gheap_is_heap_until(const struct gheap_ctx *ctx, 88 | const void *base, size_t heap_size); 89 | 90 | /* 91 | * Returns non-zero if base points to valid max heap. Returns zero otherwise. 92 | * Uses less_comparer for items' comparison. 93 | */ 94 | static inline int gheap_is_heap(const struct gheap_ctx *ctx, 95 | const void *base, size_t heap_size); 96 | 97 | /* 98 | * Makes max heap from items base[0] ... base[heap_size-1]. 99 | * Uses less_comparer for items' comparison. 100 | */ 101 | static inline void gheap_make_heap(const struct gheap_ctx *ctx, 102 | void *base, size_t heap_size); 103 | 104 | /* 105 | * Pushes the item base[heap_size-1] into max heap base[0] ... base[heap_size-2] 106 | * Uses less_comparer for items' comparison. 107 | */ 108 | static inline void gheap_push_heap(const struct gheap_ctx *ctx, 109 | void *base, size_t heap_size); 110 | 111 | /* 112 | * Pops the maximum item from max heap base[0] ... base[heap_size-1] into 113 | * base[heap_size-1]. 114 | * Uses less_comparer for items' comparison. 115 | */ 116 | static inline void gheap_pop_heap(const struct gheap_ctx *ctx, 117 | void *base, size_t heap_size); 118 | 119 | /* 120 | * Sorts items in place of max heap in ascending order. 121 | * Uses less_comparer for items' comparison. 122 | */ 123 | static inline void gheap_sort_heap(const struct gheap_ctx *ctx, 124 | void *base, size_t heap_size); 125 | 126 | /* 127 | * Swaps the item outside the heap with the maximum item inside 128 | * the heap and restores heap invariant. 129 | */ 130 | static inline void gheap_swap_max_item(const struct gheap_ctx *const ctx, 131 | void *const base, const size_t heap_size, void *item); 132 | 133 | /* 134 | * Restores max heap invariant after item's value has been increased, 135 | * i.e. less_comparer(old_item, new_item) != 0. 136 | */ 137 | static inline void gheap_restore_heap_after_item_increase( 138 | const struct gheap_ctx *ctx, 139 | void *base, size_t heap_size, size_t modified_item_index); 140 | 141 | /* 142 | * Restores max heap invariant after item's value has been decreased, 143 | * i.e. less_comparer(new_item, old_item) != 0. 144 | */ 145 | static inline void gheap_restore_heap_after_item_decrease( 146 | const struct gheap_ctx *ctx, 147 | void *base, size_t heap_size, size_t modified_item_index); 148 | 149 | /* 150 | * Removes the given item from the heap and puts it into base[heap_size-1]. 151 | * Uses less_comparer for items' comparison. 152 | */ 153 | static inline void gheap_remove_from_heap(const struct gheap_ctx *ctx, 154 | void *base, size_t heap_size, size_t item_index); 155 | 156 | /******************************************************************************* 157 | * Implementation. 158 | * 159 | * Define all functions inline, so compiler will be able optimizing out common 160 | * args (fanout, page_chunks, item_size, less_comparer and item_mover), 161 | * which are usually constants, using contant folding optimization 162 | * ( http://en.wikipedia.org/wiki/Constant_folding ). 163 | *****************************************************************************/ 164 | 165 | #include /* for assert */ 166 | #include /* for size_t */ 167 | #include /* for uintptr_t, SIZE_MAX and UINTPTR_MAX */ 168 | 169 | static inline size_t gheap_get_parent_index(const struct gheap_ctx *const ctx, 170 | size_t u) 171 | { 172 | assert(u > 0); 173 | 174 | const size_t fanout = ctx->fanout; 175 | const size_t page_chunks = ctx->page_chunks; 176 | 177 | --u; 178 | if (page_chunks == 1) { 179 | return u / fanout; 180 | } 181 | 182 | if (u < fanout) { 183 | /* Parent is root. */ 184 | return 0; 185 | } 186 | 187 | assert(page_chunks <= SIZE_MAX / fanout); 188 | const size_t page_size = fanout * page_chunks; 189 | size_t v = u % page_size; 190 | if (v >= fanout) { 191 | /* Fast path. Parent is on the same page as the child. */ 192 | return u - v + v / fanout; 193 | } 194 | 195 | /* Slow path. Parent is on another page. */ 196 | v = u / page_size - 1; 197 | const size_t page_leaves = (fanout - 1) * page_chunks + 1; 198 | u = v / page_leaves + 1; 199 | return u * page_size + v % page_leaves - page_leaves + 1; 200 | } 201 | 202 | static inline size_t gheap_get_child_index(const struct gheap_ctx *const ctx, 203 | size_t u) 204 | { 205 | assert(u < SIZE_MAX); 206 | 207 | const size_t fanout = ctx->fanout; 208 | const size_t page_chunks = ctx->page_chunks; 209 | 210 | if (page_chunks == 1) { 211 | if (u > (SIZE_MAX - 1) / fanout) { 212 | /* Child overflow. */ 213 | return SIZE_MAX; 214 | } 215 | return u * fanout + 1; 216 | } 217 | 218 | if (u == 0) { 219 | /* Root's child is always 1. */ 220 | return 1; 221 | } 222 | 223 | assert(page_chunks <= SIZE_MAX / fanout); 224 | const size_t page_size = fanout * page_chunks; 225 | --u; 226 | size_t v = u % page_size + 1; 227 | if (v < page_size / fanout) { 228 | /* Fast path. Child is on the same page as the parent. */ 229 | v *= fanout - 1; 230 | if (u > SIZE_MAX - 2 - v) { 231 | /* Child overflow. */ 232 | return SIZE_MAX; 233 | } 234 | return u + v + 2; 235 | } 236 | 237 | /* Slow path. Child is on another page. */ 238 | const size_t page_leaves = (fanout - 1) * page_chunks + 1; 239 | v += (u / page_size + 1) * page_leaves - page_size; 240 | if (v > (SIZE_MAX - 1) / page_size) { 241 | /* Child overflow. */ 242 | return SIZE_MAX; 243 | } 244 | return v * page_size + 1; 245 | } 246 | 247 | /* Returns a pointer to base[index]. */ 248 | static inline void *_gheap_get_item_ptr(const struct gheap_ctx *const ctx, 249 | const void *const base, const size_t index) 250 | { 251 | const size_t item_size = ctx->item_size; 252 | 253 | assert(index <= SIZE_MAX / item_size); 254 | 255 | const size_t offset = item_size * index; 256 | assert((uintptr_t)base <= UINTPTR_MAX - offset); 257 | 258 | return ((char *)base) + offset; 259 | } 260 | 261 | /* 262 | * Sifts the item up in the given sub-heap with the given root_index 263 | * starting from the hole_index. 264 | */ 265 | static inline void _gheap_sift_up(const struct gheap_ctx *const ctx, 266 | void *const base, const size_t root_index, size_t hole_index, 267 | const void *const item) 268 | { 269 | assert(hole_index >= root_index); 270 | 271 | const gheap_less_comparer_t less_comparer = ctx->less_comparer; 272 | const void *const less_comparer_ctx = ctx->less_comparer_ctx; 273 | const gheap_item_mover_t item_mover = ctx->item_mover; 274 | 275 | while (hole_index > root_index) { 276 | const size_t parent_index = gheap_get_parent_index(ctx, hole_index); 277 | assert(parent_index >= root_index); 278 | const void *const parent = _gheap_get_item_ptr(ctx, base, parent_index); 279 | if (!less_comparer(less_comparer_ctx, parent, item)) { 280 | break; 281 | } 282 | item_mover(_gheap_get_item_ptr(ctx, base, hole_index), 283 | parent); 284 | hole_index = parent_index; 285 | } 286 | 287 | item_mover(_gheap_get_item_ptr(ctx, base, hole_index), item); 288 | } 289 | 290 | /* 291 | * Moves the max child into the given hole and returns index 292 | * of the new hole. 293 | */ 294 | static inline size_t _gheap_move_up_max_child(const struct gheap_ctx *const ctx, 295 | void *const base, const size_t children_count, 296 | const size_t hole_index, const size_t child_index) 297 | { 298 | assert(children_count > 0); 299 | assert(children_count <= ctx->fanout); 300 | assert(child_index == gheap_get_child_index(ctx, hole_index)); 301 | 302 | const gheap_less_comparer_t less_comparer = ctx->less_comparer; 303 | const void *const less_comparer_ctx = ctx->less_comparer_ctx; 304 | const gheap_item_mover_t item_mover = ctx->item_mover; 305 | 306 | size_t max_child_index = child_index; 307 | for (size_t i = 1; i < children_count; ++i) { 308 | if (!less_comparer(less_comparer_ctx, 309 | _gheap_get_item_ptr(ctx, base, child_index + i), 310 | _gheap_get_item_ptr(ctx, base, max_child_index))) { 311 | max_child_index = child_index + i; 312 | } 313 | } 314 | item_mover(_gheap_get_item_ptr(ctx, base, hole_index), 315 | _gheap_get_item_ptr(ctx, base, max_child_index)); 316 | return max_child_index; 317 | } 318 | 319 | /* 320 | * Sifts the given item down in the heap of the given size starting 321 | * from the hole_index. 322 | */ 323 | static inline void _gheap_sift_down(const struct gheap_ctx *const ctx, 324 | void *const base, const size_t heap_size, size_t hole_index, 325 | const void *const item) 326 | { 327 | assert(heap_size > 0); 328 | assert(hole_index < heap_size); 329 | 330 | const size_t fanout = ctx->fanout; 331 | 332 | const size_t root_index = hole_index; 333 | const size_t last_full_index = heap_size - (heap_size - 1) % fanout; 334 | while (1) { 335 | const size_t child_index = gheap_get_child_index(ctx, hole_index); 336 | if (child_index >= last_full_index) { 337 | if (child_index < heap_size) { 338 | assert(child_index == last_full_index); 339 | hole_index = _gheap_move_up_max_child(ctx, base, 340 | heap_size - child_index, hole_index, child_index); 341 | } 342 | break; 343 | } 344 | assert(heap_size - child_index >= fanout); 345 | hole_index = _gheap_move_up_max_child(ctx, base, fanout, hole_index, 346 | child_index); 347 | } 348 | _gheap_sift_up(ctx, base, root_index, hole_index, item); 349 | } 350 | 351 | /* 352 | * Pops the maximum item from the heap [base[0] ... base[heap_size-1]] 353 | * into base[heap_size]. 354 | */ 355 | static inline void _gheap_pop_max_item(const struct gheap_ctx *const ctx, 356 | void *const base, const size_t heap_size) 357 | { 358 | void *const hole = _gheap_get_item_ptr(ctx, base, heap_size); 359 | gheap_swap_max_item(ctx, base, heap_size, hole); 360 | } 361 | 362 | static inline size_t gheap_is_heap_until(const struct gheap_ctx *const ctx, 363 | const void *const base, const size_t heap_size) 364 | { 365 | const gheap_less_comparer_t less_comparer = ctx->less_comparer; 366 | const void *const less_comparer_ctx = ctx->less_comparer_ctx; 367 | 368 | for (size_t u = 1; u < heap_size; ++u) { 369 | const size_t v = gheap_get_parent_index(ctx, u); 370 | const void *const a = _gheap_get_item_ptr(ctx, base, v); 371 | const void *const b = _gheap_get_item_ptr(ctx, base, u); 372 | if (less_comparer(less_comparer_ctx, a, b)) { 373 | return u; 374 | } 375 | } 376 | return heap_size; 377 | } 378 | 379 | static inline int gheap_is_heap(const struct gheap_ctx *const ctx, 380 | const void *const base, const size_t heap_size) 381 | { 382 | return (gheap_is_heap_until(ctx, base, heap_size) == heap_size); 383 | } 384 | 385 | static inline void gheap_make_heap(const struct gheap_ctx *const ctx, 386 | void *const base, const size_t heap_size) 387 | { 388 | const size_t fanout = ctx->fanout; 389 | const size_t page_chunks = ctx->page_chunks; 390 | const size_t item_size = ctx->item_size; 391 | const gheap_item_mover_t item_mover = ctx->item_mover; 392 | 393 | if (heap_size > 1) { 394 | /* Skip leaf nodes without children. This is easy to do for non-paged heap, 395 | * i.e. when page_chunks = 1, but it is difficult for paged heaps. 396 | * So leaf nodes in paged heaps are visited anyway. 397 | */ 398 | size_t i = (page_chunks == 1) ? ((heap_size - 2) / fanout) : 399 | (heap_size - 2); 400 | do { 401 | char tmp[item_size]; 402 | item_mover(tmp, _gheap_get_item_ptr(ctx, base, i)); 403 | _gheap_sift_down(ctx, base, heap_size, i, tmp); 404 | } while (i-- > 0); 405 | } 406 | 407 | assert(gheap_is_heap(ctx, base, heap_size)); 408 | } 409 | 410 | static inline void gheap_push_heap(const struct gheap_ctx *const ctx, 411 | void *const base, const size_t heap_size) 412 | { 413 | assert(heap_size > 0); 414 | assert(gheap_is_heap(ctx, base, heap_size - 1)); 415 | 416 | const size_t item_size = ctx->item_size; 417 | const gheap_item_mover_t item_mover = ctx->item_mover; 418 | 419 | if (heap_size > 1) { 420 | const size_t u = heap_size - 1; 421 | char tmp[item_size]; 422 | item_mover(tmp, _gheap_get_item_ptr(ctx, base, u)); 423 | _gheap_sift_up(ctx, base, 0, u, tmp); 424 | } 425 | 426 | assert(gheap_is_heap(ctx, base, heap_size)); 427 | } 428 | 429 | static inline void gheap_pop_heap(const struct gheap_ctx *const ctx, 430 | void *const base, const size_t heap_size) 431 | { 432 | assert(heap_size > 0); 433 | assert(gheap_is_heap(ctx, base, heap_size)); 434 | 435 | if (heap_size > 1) { 436 | _gheap_pop_max_item(ctx, base, heap_size - 1); 437 | } 438 | 439 | assert(gheap_is_heap(ctx, base, heap_size - 1)); 440 | } 441 | 442 | static inline void gheap_sort_heap(const struct gheap_ctx *const ctx, 443 | void *const base, const size_t heap_size) 444 | { 445 | for (size_t i = heap_size; i > 1; --i) { 446 | _gheap_pop_max_item(ctx, base, i - 1); 447 | } 448 | } 449 | 450 | static inline void gheap_swap_max_item(const struct gheap_ctx *const ctx, 451 | void *const base, const size_t heap_size, void *item) 452 | { 453 | assert(heap_size > 0); 454 | assert(gheap_is_heap(ctx, base, heap_size)); 455 | 456 | const size_t item_size = ctx->item_size; 457 | const gheap_item_mover_t item_mover = ctx->item_mover; 458 | 459 | char tmp[item_size]; 460 | item_mover(tmp, item); 461 | item_mover(item, base); 462 | _gheap_sift_down(ctx, base, heap_size, 0, tmp); 463 | 464 | assert(gheap_is_heap(ctx, base, heap_size)); 465 | } 466 | 467 | static inline void gheap_restore_heap_after_item_increase( 468 | const struct gheap_ctx *const ctx, 469 | void *const base, const size_t heap_size, size_t modified_item_index) 470 | { 471 | assert(heap_size > 0); 472 | assert(modified_item_index < heap_size); 473 | assert(gheap_is_heap(ctx, base, modified_item_index)); 474 | 475 | const size_t item_size = ctx->item_size; 476 | const gheap_item_mover_t item_mover = ctx->item_mover; 477 | 478 | if (modified_item_index > 0) { 479 | char tmp[item_size]; 480 | item_mover(tmp, _gheap_get_item_ptr(ctx, base, modified_item_index)); 481 | _gheap_sift_up(ctx, base, 0, modified_item_index, tmp); 482 | } 483 | 484 | assert(gheap_is_heap(ctx, base, heap_size)); 485 | (void)heap_size; 486 | } 487 | 488 | static inline void gheap_restore_heap_after_item_decrease( 489 | const struct gheap_ctx *const ctx, 490 | void *const base, const size_t heap_size, size_t modified_item_index) 491 | { 492 | assert(heap_size > 0); 493 | assert(modified_item_index < heap_size); 494 | assert(gheap_is_heap(ctx, base, modified_item_index)); 495 | 496 | const size_t item_size = ctx->item_size; 497 | const gheap_item_mover_t item_mover = ctx->item_mover; 498 | 499 | char tmp[item_size]; 500 | item_mover(tmp, _gheap_get_item_ptr(ctx, base, modified_item_index)); 501 | _gheap_sift_down(ctx, base, heap_size, modified_item_index, tmp); 502 | 503 | assert(gheap_is_heap(ctx, base, heap_size)); 504 | } 505 | 506 | static inline void gheap_remove_from_heap(const struct gheap_ctx *const ctx, 507 | void *const base, const size_t heap_size, size_t item_index) 508 | { 509 | assert(heap_size > 0); 510 | assert(item_index < heap_size); 511 | assert(gheap_is_heap(ctx, base, heap_size)); 512 | 513 | const size_t item_size = ctx->item_size; 514 | const gheap_less_comparer_t less_comparer = ctx->less_comparer; 515 | const void *const less_comparer_ctx = ctx->less_comparer_ctx; 516 | const gheap_item_mover_t item_mover = ctx->item_mover; 517 | 518 | const size_t new_heap_size = heap_size - 1; 519 | if (item_index < new_heap_size) { 520 | char tmp[item_size]; 521 | void *const hole = _gheap_get_item_ptr(ctx, base, new_heap_size); 522 | item_mover(tmp, hole); 523 | item_mover(hole, _gheap_get_item_ptr(ctx, base, item_index)); 524 | if (less_comparer(less_comparer_ctx, tmp, hole)) { 525 | _gheap_sift_down(ctx, base, new_heap_size, item_index, tmp); 526 | } 527 | else { 528 | _gheap_sift_up(ctx, base, 0, item_index, tmp); 529 | } 530 | } 531 | 532 | assert(gheap_is_heap(ctx, base, new_heap_size)); 533 | } 534 | 535 | #endif 536 | -------------------------------------------------------------------------------- /gheap.hpp: -------------------------------------------------------------------------------- 1 | // Pass -DGHEAP_CPP11 to compiler for including gheap optimized for C++11. 2 | // Otherwise gheap optimized for C++03 will be included. 3 | 4 | #ifdef GHEAP_CPP11 5 | # include "gheap_cpp11.hpp" 6 | #else 7 | # include "gheap_cpp03.hpp" 8 | #endif 9 | -------------------------------------------------------------------------------- /gheap_cpp03.hpp: -------------------------------------------------------------------------------- 1 | #ifndef GHEAP_H 2 | #define GHEAP_H 3 | 4 | // Generalized heap implementation for C++03. 5 | // 6 | // The implementation relies on std::swap<>() specializations, 7 | // so provide swap() specializations for classes with expensive copy 8 | // constructor and/or copy assignment operator. 9 | // 10 | // Use gheap_cpp11.hpp instead if your compiler supports C++11. 11 | // See http://en.wikipedia.org/wiki/C%2B%2B11 for details. 12 | // The implementation for C++11 is usually faster than the implementation 13 | // for C++03. 14 | // 15 | // Don't forget passing -DNDEBUG option to the compiler when creating optimized 16 | // builds. This significantly speeds up gheap code by removing debug assertions. 17 | // 18 | // Author: Aliaksandr Valialkin . 19 | 20 | #include // for std::swap() 21 | #include // for assert 22 | #include // for size_t 23 | #include // for std::iterator_traits 24 | 25 | // C++03 has no SIZE_MAX, so define it here. 26 | #ifndef SIZE_MAX 27 | # define SIZE_MAX (~(size_t)0) 28 | #endif 29 | 30 | template 31 | class gheap 32 | { 33 | public: 34 | 35 | static const size_t FANOUT = Fanout; 36 | static const size_t PAGE_CHUNKS = PageChunks; 37 | static const size_t PAGE_SIZE = Fanout * PageChunks; 38 | 39 | // Returns parent index for the given child index. 40 | // Child index must be greater than 0. 41 | // Returns 0 if the parent is root. 42 | static size_t get_parent_index(size_t u) 43 | { 44 | assert(u > 0); 45 | 46 | --u; 47 | if (PageChunks == 1) { 48 | return u / Fanout; 49 | } 50 | 51 | if (u < Fanout) { 52 | // Parent is root. 53 | return 0; 54 | } 55 | 56 | assert(PageChunks <= SIZE_MAX / Fanout); 57 | const size_t page_size = Fanout * PageChunks; 58 | size_t v = u % page_size; 59 | if (v >= Fanout) { 60 | // Fast path. Parent is on the same page as the child. 61 | return u - v + v / Fanout; 62 | } 63 | 64 | // Slow path. Parent is on another page. 65 | v = u / page_size - 1; 66 | const size_t page_leaves = (Fanout - 1) * PageChunks + 1; 67 | u = v / page_leaves + 1; 68 | return u * page_size + v % page_leaves - page_leaves + 1; 69 | } 70 | 71 | // Returns the index of the first child for the given parent index. 72 | // Parent index must be less than SIZE_MAX. 73 | // Returns SIZE_MAX if the index of the first child for the given parent 74 | // cannot fit size_t. 75 | static size_t get_child_index(size_t u) 76 | { 77 | assert(u < SIZE_MAX); 78 | 79 | if (PageChunks == 1) { 80 | if (u > (SIZE_MAX - 1) / Fanout) { 81 | // Child overflow. 82 | return SIZE_MAX; 83 | } 84 | return u * Fanout + 1; 85 | } 86 | 87 | if (u == 0) { 88 | // Root's child is always 1. 89 | return 1; 90 | } 91 | 92 | assert(PageChunks <= SIZE_MAX / Fanout); 93 | const size_t page_size = Fanout * PageChunks; 94 | --u; 95 | size_t v = u % page_size + 1; 96 | if (v < page_size / Fanout) { 97 | // Fast path. Child is on the same page as the parent. 98 | v *= Fanout - 1; 99 | if (u > SIZE_MAX - 2 - v) { 100 | // Child overflow. 101 | return SIZE_MAX; 102 | } 103 | return u + v + 2; 104 | } 105 | 106 | // Slow path. Child is on another page. 107 | const size_t page_leaves = (Fanout - 1) * PageChunks + 1; 108 | v += (u / page_size + 1) * page_leaves - page_size; 109 | if (v > (SIZE_MAX - 1) / page_size) { 110 | // Child overflow. 111 | return SIZE_MAX; 112 | } 113 | return v * page_size + 1; 114 | } 115 | 116 | private: 117 | 118 | template 119 | static void _swap(const T &a, const T &b) 120 | { 121 | // a and b are const for optimization purposes only. This hints compiler 122 | // that values referenced by a and b cannot be modified by somebody else, 123 | // so it is safe reading these values from CPU registers instead of reading 124 | // them from slow memory on each read access. 125 | // 126 | // Of course, this optimization works only if values are small enough 127 | // to fit CPU registers. 128 | std::swap(const_cast(a), const_cast(b)); 129 | } 130 | 131 | // Sifts the item up in the given sub-heap with the given root_index 132 | // starting from the item_index. 133 | template 134 | static void _sift_up(const RandomAccessIterator &first, 135 | const LessComparer &less_comparer, 136 | const size_t root_index, size_t item_index) 137 | { 138 | assert(item_index >= root_index); 139 | 140 | typedef typename std::iterator_traits::value_type 141 | value_type; 142 | 143 | while (item_index > root_index) { 144 | const size_t parent_index = get_parent_index(item_index); 145 | assert(parent_index >= root_index); 146 | const value_type &item = first[item_index]; 147 | const value_type &parent = first[parent_index]; 148 | if (!less_comparer(parent, item)) { 149 | break; 150 | } 151 | _swap(item, parent); 152 | item_index = parent_index; 153 | } 154 | } 155 | 156 | // Swaps the max child with the item at item_index and returns index 157 | // of the max child. 158 | template 159 | static size_t _move_up_max_child(const RandomAccessIterator &first, 160 | const LessComparer &less_comparer, const size_t children_count, 161 | const size_t item_index, const size_t child_index) 162 | { 163 | assert(children_count > 0); 164 | assert(children_count <= Fanout); 165 | assert(child_index == get_child_index(item_index)); 166 | 167 | size_t max_child_index = child_index; 168 | for (size_t i = 1; i < children_count; ++i) { 169 | if (!less_comparer(first[child_index + i], first[max_child_index])) { 170 | max_child_index = child_index + i; 171 | } 172 | } 173 | _swap(first[item_index], first[max_child_index]); 174 | return max_child_index; 175 | } 176 | 177 | // Sifts the given item down in the heap of the given size starting 178 | // from the item_index. 179 | template 180 | static void _sift_down(const RandomAccessIterator &first, 181 | const LessComparer &less_comparer, 182 | const size_t heap_size, size_t item_index) 183 | { 184 | assert(heap_size > 0); 185 | assert(item_index < heap_size); 186 | 187 | const size_t root_index = item_index; 188 | const size_t last_full_index = heap_size - (heap_size - 1) % Fanout; 189 | while (true) { 190 | const size_t child_index = get_child_index(item_index); 191 | if (child_index >= last_full_index) { 192 | if (child_index < heap_size) { 193 | assert(child_index == last_full_index); 194 | item_index = _move_up_max_child(first, less_comparer, 195 | heap_size - child_index, item_index, child_index); 196 | } 197 | break; 198 | } 199 | assert(heap_size - child_index >= Fanout); 200 | item_index = _move_up_max_child(first, less_comparer, Fanout, 201 | item_index, child_index); 202 | } 203 | _sift_up(first, less_comparer, root_index, item_index); 204 | } 205 | 206 | // Standard less comparer. 207 | template 208 | static bool _std_less_comparer( 209 | const typename std::iterator_traits::value_type &a, 210 | const typename std::iterator_traits::value_type &b) 211 | { 212 | return (a < b); 213 | } 214 | 215 | // Pops max item from the heap [first[0] ... first[heap_size-1]] 216 | // into first[heap_size]. 217 | template 218 | static void _pop_max_item(const RandomAccessIterator &first, 219 | const LessComparer &less_comparer, const size_t heap_size) 220 | { 221 | assert(heap_size > 0); 222 | 223 | _swap(first[heap_size], first[0]); 224 | _sift_down(first, less_comparer, heap_size, 0); 225 | } 226 | 227 | public: 228 | 229 | // Returns an iterator for the first non-heap item in the range 230 | // [first ... last) using less_comparer for items' comparison. 231 | // Returns last if the range contains valid max heap. 232 | template 233 | static RandomAccessIterator is_heap_until( 234 | const RandomAccessIterator &first, const RandomAccessIterator &last, 235 | const LessComparer &less_comparer) 236 | { 237 | assert(last >= first); 238 | 239 | const size_t heap_size = last - first; 240 | for (size_t u = 1; u < heap_size; ++u) { 241 | const size_t v = get_parent_index(u); 242 | if (less_comparer(first[v], first[u])) { 243 | return first + u; 244 | } 245 | } 246 | return last; 247 | } 248 | 249 | // Returns an iterator for the first non-heap item in the range 250 | // [first ... last) using operator< for items' comparison. 251 | // Returns last if the range contains valid max heap. 252 | template 253 | static RandomAccessIterator is_heap_until( 254 | const RandomAccessIterator &first, const RandomAccessIterator &last) 255 | { 256 | return is_heap_until(first, last, _std_less_comparer); 257 | } 258 | 259 | // Returns true if the range [first ... last) contains valid max heap. 260 | // Returns false otherwise. 261 | // Uses less_comparer for items' comparison. 262 | template 263 | static bool is_heap(const RandomAccessIterator &first, 264 | const RandomAccessIterator &last, const LessComparer &less_comparer) 265 | { 266 | return (is_heap_until(first, last, less_comparer) == last); 267 | } 268 | 269 | // Returns true if the range [first ... last) contains valid max heap. 270 | // Returns false otherwise. 271 | // Uses operator< for items' comparison. 272 | template 273 | static bool is_heap(const RandomAccessIterator &first, 274 | const RandomAccessIterator &last) 275 | { 276 | return is_heap(first, last, _std_less_comparer); 277 | } 278 | 279 | // Makes max heap from items [first ... last) using the given less_comparer 280 | // for items' comparison. 281 | template 282 | static void make_heap(const RandomAccessIterator &first, 283 | const RandomAccessIterator &last, const LessComparer &less_comparer) 284 | { 285 | assert(last >= first); 286 | 287 | const size_t heap_size = last - first; 288 | if (heap_size > 1) { 289 | // Skip leaf nodes without children. This is easy to do for non-paged 290 | // heap, i.e. when page_chunks = 1, but it is difficult for paged heaps. 291 | // So leaf nodes in paged heaps are visited anyway. 292 | size_t i = (PageChunks == 1) ? ((heap_size - 2) / Fanout) : 293 | (heap_size - 2); 294 | do { 295 | _sift_down(first, less_comparer, heap_size, i); 296 | } while (i-- > 0); 297 | } 298 | 299 | assert(is_heap(first, last, less_comparer)); 300 | } 301 | 302 | // Makes max heap from items [first ... last) using operator< for items' 303 | // comparison. 304 | template 305 | static void make_heap(const RandomAccessIterator &first, 306 | const RandomAccessIterator &last) 307 | { 308 | make_heap(first, last, _std_less_comparer); 309 | } 310 | 311 | // Pushes the item *(last - 1) into max heap [first ... last - 1) 312 | // using the given less_comparer for items' comparison. 313 | template 314 | static void push_heap(const RandomAccessIterator &first, 315 | const RandomAccessIterator &last, const LessComparer &less_comparer) 316 | { 317 | assert(last > first); 318 | assert(is_heap(first, last - 1, less_comparer)); 319 | 320 | const size_t heap_size = last - first; 321 | if (heap_size > 1) { 322 | const size_t u = heap_size - 1; 323 | _sift_up(first, less_comparer, 0, u); 324 | } 325 | 326 | assert(is_heap(first, last, less_comparer)); 327 | } 328 | 329 | // Pushes the item *(last - 1) into max heap [first ... last - 1) 330 | // using operator< for items' comparison. 331 | template 332 | static void push_heap(const RandomAccessIterator &first, 333 | const RandomAccessIterator &last) 334 | { 335 | push_heap(first, last, _std_less_comparer); 336 | } 337 | 338 | // Pops the maximum item from max heap [first ... last) into 339 | // *(last - 1) using the given less_comparer for items' comparison. 340 | template 341 | static void pop_heap(const RandomAccessIterator &first, 342 | const RandomAccessIterator &last, const LessComparer &less_comparer) 343 | { 344 | assert(last > first); 345 | assert(is_heap(first, last, less_comparer)); 346 | 347 | const size_t heap_size = last - first; 348 | if (heap_size > 1) { 349 | _pop_max_item(first, less_comparer, heap_size - 1); 350 | } 351 | 352 | assert(is_heap(first, last - 1, less_comparer)); 353 | } 354 | 355 | // Pops the maximum item from max heap [first ... last) into 356 | // *(last - 1) using operator< for items' comparison. 357 | template 358 | static void pop_heap(const RandomAccessIterator &first, 359 | const RandomAccessIterator &last) 360 | { 361 | pop_heap(first, last, _std_less_comparer); 362 | } 363 | 364 | // Sorts max heap [first ... last) using the given less_comparer 365 | // for items' comparison. 366 | // Items are sorted in ascending order. 367 | template 368 | static void sort_heap(const RandomAccessIterator &first, 369 | const RandomAccessIterator &last, const LessComparer &less_comparer) 370 | { 371 | assert(last >= first); 372 | 373 | const size_t heap_size = last - first; 374 | for (size_t i = heap_size; i > 1; --i) { 375 | _pop_max_item(first, less_comparer, i - 1); 376 | } 377 | } 378 | 379 | // Sorts max heap [first ... last) using operator< for items' comparison. 380 | // Items are sorted in ascending order. 381 | template 382 | static void sort_heap(const RandomAccessIterator &first, 383 | const RandomAccessIterator &last) 384 | { 385 | sort_heap(first, last, _std_less_comparer); 386 | } 387 | 388 | // Swaps the item outside the heap with the maximum item inside 389 | // the heap [first ... last) and restores the heap invariant. 390 | // Uses less_comparer for items' comparisons. 391 | template 392 | static void swap_max_item(const RandomAccessIterator &first, 393 | const RandomAccessIterator &last, 394 | typename std::iterator_traits::value_type &item, 395 | const LessComparer &less_comparer) 396 | { 397 | assert(first < last); 398 | assert(is_heap(first, last, less_comparer)); 399 | 400 | const size_t heap_size = last - first; 401 | 402 | _swap(item, first[0]); 403 | _sift_down(first, less_comparer, heap_size, 0); 404 | 405 | assert(is_heap(first, last, less_comparer)); 406 | } 407 | 408 | // Swaps the item outside the heap with the maximum item inside 409 | // the heap [first ... last) and restores the heap invariant. 410 | // Uses operator< for items' comparisons. 411 | template 412 | static void swap_max_item(const RandomAccessIterator &first, 413 | const RandomAccessIterator &last, 414 | typename std::iterator_traits::value_type &item) 415 | { 416 | swap_max_item(first, last, item, _std_less_comparer); 417 | } 418 | 419 | // Restores max heap invariant after item's value has been increased, 420 | // i.e. less_comparer(old_item, new_item) == true. 421 | template 422 | static void restore_heap_after_item_increase( 423 | const RandomAccessIterator &first, const RandomAccessIterator &item, 424 | const LessComparer &less_comparer) 425 | { 426 | assert(item >= first); 427 | assert(is_heap(first, item, less_comparer)); 428 | 429 | const size_t item_index = item - first; 430 | if (item_index > 0) { 431 | _sift_up(first, less_comparer, 0, item_index); 432 | } 433 | 434 | assert(is_heap(first, item + 1, less_comparer)); 435 | } 436 | 437 | // Restores max heap invariant after item's value has been increased, 438 | // i.e. old_item < new_item. 439 | template 440 | static void restore_heap_after_item_increase( 441 | const RandomAccessIterator &first, const RandomAccessIterator &item) 442 | { 443 | restore_heap_after_item_increase(first, item, 444 | _std_less_comparer); 445 | } 446 | 447 | // Restores max heap invariant after item's value has been decreased, 448 | // i.e. less_comparer(new_item, old_item) == true. 449 | template 450 | static void restore_heap_after_item_decrease( 451 | const RandomAccessIterator &first, const RandomAccessIterator &item, 452 | const RandomAccessIterator &last, const LessComparer &less_comparer) 453 | { 454 | assert(last > first); 455 | assert(item >= first); 456 | assert(item < last); 457 | assert(is_heap(first, item, less_comparer)); 458 | 459 | const size_t heap_size = last - first; 460 | const size_t item_index = item - first; 461 | _sift_down(first, less_comparer, heap_size, item_index); 462 | 463 | assert(is_heap(first, last, less_comparer)); 464 | } 465 | 466 | // Restores max heap invariant after item's value has been decreased, 467 | // i.e. new_item < old_item. 468 | template 469 | static void restore_heap_after_item_decrease( 470 | const RandomAccessIterator &first, const RandomAccessIterator &item, 471 | const RandomAccessIterator &last) 472 | { 473 | restore_heap_after_item_decrease(first, item, last, 474 | _std_less_comparer); 475 | } 476 | 477 | // Removes the given item from the heap and puts it into *(last - 1). 478 | // less_comparer is used for items' comparison. 479 | template 480 | static void remove_from_heap(const RandomAccessIterator &first, 481 | const RandomAccessIterator &item, const RandomAccessIterator &last, 482 | const LessComparer &less_comparer) 483 | { 484 | assert(last > first); 485 | assert(item >= first); 486 | assert(item < last); 487 | assert(is_heap(first, last, less_comparer)); 488 | 489 | const size_t new_heap_size = last - first - 1; 490 | const size_t item_index = item - first; 491 | if (item_index < new_heap_size) { 492 | _swap(*item, first[new_heap_size]); 493 | if (less_comparer(*item, first[new_heap_size])) { 494 | _sift_down(first, less_comparer, new_heap_size, item_index); 495 | } 496 | else { 497 | _sift_up(first, less_comparer, 0, item_index); 498 | } 499 | } 500 | 501 | assert(is_heap(first, last - 1, less_comparer)); 502 | } 503 | 504 | // Removes the given item from the heap and puts it into *(last - 1). 505 | // operator< is used for items' comparison. 506 | template 507 | static void remove_from_heap(const RandomAccessIterator &first, 508 | const RandomAccessIterator &item, const RandomAccessIterator &last) 509 | { 510 | remove_from_heap(first, item, last, 511 | _std_less_comparer); 512 | } 513 | }; 514 | 515 | #endif 516 | -------------------------------------------------------------------------------- /gheap_cpp11.hpp: -------------------------------------------------------------------------------- 1 | #ifndef GHEAP_H 2 | #define GHEAP_H 3 | 4 | // Generalized heap implementation for C++11. 5 | // The implementation requires the following C++11 features: 6 | // - must contain SIZE_MAX definition. 7 | // - std::move() support. The implementation relies on move constructors 8 | // and move assignment operators, so define them for classes with expensive 9 | // copy constructors and copy assignment operators. 10 | // See http://en.wikipedia.org/wiki/C%2B%2B11 for details. 11 | // 12 | // Use gheap_cpp03.hpp instead if your compiler doesn't support these features. 13 | // The implementation for C++11 is usually faster than the implementation 14 | // for C++03. 15 | // 16 | // Don't forget passing -DNDEBUG option to the compiler when creating optimized 17 | // builds. This significantly speeds up gheap code by removing debug assertions. 18 | // 19 | // Author: Aliaksandr Valialkin . 20 | 21 | #include // for assert 22 | #include // for size_t 23 | #include // for SIZE_MAX 24 | #include // for std::iterator_traits 25 | #include // for std::move() 26 | 27 | template 28 | class gheap 29 | { 30 | public: 31 | 32 | static const size_t FANOUT = Fanout; 33 | static const size_t PAGE_CHUNKS = PageChunks; 34 | static const size_t PAGE_SIZE = Fanout * PageChunks; 35 | 36 | // Returns parent index for the given child index. 37 | // Child index must be greater than 0. 38 | // Returns 0 if the parent is root. 39 | static size_t get_parent_index(size_t u) 40 | { 41 | assert(u > 0); 42 | 43 | --u; 44 | if (PageChunks == 1) { 45 | return u / Fanout; 46 | } 47 | 48 | if (u < Fanout) { 49 | // Parent is root. 50 | return 0; 51 | } 52 | 53 | assert(PageChunks <= SIZE_MAX / Fanout); 54 | const size_t page_size = Fanout * PageChunks; 55 | size_t v = u % page_size; 56 | if (v >= Fanout) { 57 | // Fast path. Parent is on the same page as the child. 58 | return u - v + v / Fanout; 59 | } 60 | 61 | // Slow path. Parent is on another page. 62 | v = u / page_size - 1; 63 | const size_t page_leaves = (Fanout - 1) * PageChunks + 1; 64 | u = v / page_leaves + 1; 65 | return u * page_size + v % page_leaves - page_leaves + 1; 66 | } 67 | 68 | // Returns the index of the first child for the given parent index. 69 | // Parent index must be less than SIZE_MAX. 70 | // Returns SIZE_MAX if the index of the first child for the given parent 71 | // cannot fit size_t. 72 | static size_t get_child_index(size_t u) 73 | { 74 | assert(u < SIZE_MAX); 75 | 76 | if (PageChunks == 1) { 77 | if (u > (SIZE_MAX - 1) / Fanout) { 78 | // Child overflow. 79 | return SIZE_MAX; 80 | } 81 | return u * Fanout + 1; 82 | } 83 | 84 | if (u == 0) { 85 | // Root's child is always 1. 86 | return 1; 87 | } 88 | 89 | assert(PageChunks <= SIZE_MAX / Fanout); 90 | const size_t page_size = Fanout * PageChunks; 91 | --u; 92 | size_t v = u % page_size + 1; 93 | if (v < page_size / Fanout) { 94 | // Fast path. Child is on the same page as the parent. 95 | v *= Fanout - 1; 96 | if (u > SIZE_MAX - 2 - v) { 97 | // Child overflow. 98 | return SIZE_MAX; 99 | } 100 | return u + v + 2; 101 | } 102 | 103 | // Slow path. Child is on another page. 104 | const size_t page_leaves = (Fanout - 1) * PageChunks + 1; 105 | v += (u / page_size + 1) * page_leaves - page_size; 106 | if (v > (SIZE_MAX - 1) / page_size) { 107 | // Child overflow. 108 | return SIZE_MAX; 109 | } 110 | return v * page_size + 1; 111 | } 112 | 113 | private: 114 | 115 | // moves the value from src to dst. 116 | template 117 | static void _move(T &dst, const T &src) 118 | { 119 | // src is const for optimization purposes only. This hints compiler 120 | // that the value referenced by src cannot be modified by somebody else, 121 | // so it is safe reading the value from a register instead of reading it 122 | // from slow memory on each read access. 123 | // 124 | // Of course, this optimization works only for values small enough to fit 125 | // CPU registers. 126 | dst = std::move(const_cast(src)); 127 | } 128 | 129 | // Sifts the item up in the given sub-heap with the given root_index 130 | // starting from the hole_index. 131 | template 132 | static void _sift_up(const RandomAccessIterator &first, 133 | const LessComparer &less_comparer, 134 | const size_t root_index, size_t hole_index, 135 | const typename std::iterator_traits::value_type 136 | &item) 137 | { 138 | assert(hole_index >= root_index); 139 | 140 | typedef typename std::iterator_traits::value_type 141 | value_type; 142 | 143 | while (hole_index > root_index) { 144 | const size_t parent_index = get_parent_index(hole_index); 145 | assert(parent_index >= root_index); 146 | const value_type &parent = first[parent_index]; 147 | if (!less_comparer(parent, item)) { 148 | break; 149 | } 150 | _move(first[hole_index], parent); 151 | hole_index = parent_index; 152 | } 153 | _move(first[hole_index], item); 154 | } 155 | 156 | // Moves the max child into the given hole and returns index 157 | // of the new hole. 158 | template 159 | static size_t _move_up_max_child(const RandomAccessIterator &first, 160 | const LessComparer &less_comparer, const size_t children_count, 161 | const size_t hole_index, const size_t child_index) 162 | { 163 | assert(children_count > 0); 164 | assert(children_count <= Fanout); 165 | assert(child_index == get_child_index(hole_index)); 166 | 167 | size_t max_child_index = child_index; 168 | for (size_t i = 1; i < children_count; ++i) { 169 | if (!less_comparer(first[child_index + i], first[max_child_index])) { 170 | max_child_index = child_index + i; 171 | } 172 | } 173 | _move(first[hole_index], first[max_child_index]); 174 | return max_child_index; 175 | } 176 | 177 | // Sifts the given item down in the heap of the given size starting 178 | // from the hole_index. 179 | template 180 | static void _sift_down(const RandomAccessIterator &first, 181 | const LessComparer &less_comparer, 182 | const size_t heap_size, size_t hole_index, 183 | const typename std::iterator_traits::value_type 184 | &item) 185 | { 186 | assert(heap_size > 0); 187 | assert(hole_index < heap_size); 188 | 189 | const size_t root_index = hole_index; 190 | const size_t last_full_index = heap_size - (heap_size - 1) % Fanout; 191 | while (true) { 192 | const size_t child_index = get_child_index(hole_index); 193 | if (child_index >= last_full_index) { 194 | if (child_index < heap_size) { 195 | assert(child_index == last_full_index); 196 | hole_index = _move_up_max_child(first, less_comparer, 197 | heap_size - child_index, hole_index, child_index); 198 | } 199 | break; 200 | } 201 | assert(heap_size - child_index >= Fanout); 202 | hole_index = _move_up_max_child(first, less_comparer, Fanout, 203 | hole_index, child_index); 204 | } 205 | _sift_up(first, less_comparer, root_index, hole_index, item); 206 | } 207 | 208 | // Standard less comparer. 209 | template 210 | static bool _std_less_comparer( 211 | const typename std::iterator_traits::value_type &a, 212 | const typename std::iterator_traits::value_type &b) 213 | { 214 | return (a < b); 215 | } 216 | 217 | // Pops max item from the heap [first[0] ... first[heap_size-1]] 218 | // into first[heap_size]. 219 | template 220 | static void _pop_max_item(const RandomAccessIterator &first, 221 | const LessComparer &less_comparer, const size_t heap_size) 222 | { 223 | assert(heap_size > 0); 224 | 225 | typedef typename std::iterator_traits::value_type 226 | value_type; 227 | 228 | value_type tmp = std::move(first[heap_size]); 229 | _move(first[heap_size], first[0]); 230 | _sift_down(first, less_comparer, heap_size, 0, tmp); 231 | } 232 | 233 | public: 234 | 235 | // Returns an iterator for the first non-heap item in the range 236 | // [first ... last) using less_comparer for items' comparison. 237 | // Returns last if the range contains valid max heap. 238 | template 239 | static RandomAccessIterator is_heap_until( 240 | const RandomAccessIterator &first, const RandomAccessIterator &last, 241 | const LessComparer &less_comparer) 242 | { 243 | assert(last >= first); 244 | 245 | const size_t heap_size = last - first; 246 | for (size_t u = 1; u < heap_size; ++u) { 247 | const size_t v = get_parent_index(u); 248 | if (less_comparer(first[v], first[u])) { 249 | return first + u; 250 | } 251 | } 252 | return last; 253 | } 254 | 255 | // Returns an iterator for the first non-heap item in the range 256 | // [first ... last) using operator< for items' comparison. 257 | // Returns last if the range contains valid max heap. 258 | template 259 | static RandomAccessIterator is_heap_until( 260 | const RandomAccessIterator &first, const RandomAccessIterator &last) 261 | { 262 | return is_heap_until(first, last, _std_less_comparer); 263 | } 264 | 265 | // Returns true if the range [first ... last) contains valid max heap. 266 | // Returns false otherwise. 267 | // Uses less_comparer for items' comparison. 268 | template 269 | static bool is_heap(const RandomAccessIterator &first, 270 | const RandomAccessIterator &last, const LessComparer &less_comparer) 271 | { 272 | return (is_heap_until(first, last, less_comparer) == last); 273 | } 274 | 275 | // Returns true if the range [first ... last) contains valid max heap. 276 | // Returns false otherwise. 277 | // Uses operator< for items' comparison. 278 | template 279 | static bool is_heap(const RandomAccessIterator &first, 280 | const RandomAccessIterator &last) 281 | { 282 | return is_heap(first, last, _std_less_comparer); 283 | } 284 | 285 | // Makes max heap from items [first ... last) using the given less_comparer 286 | // for items' comparison. 287 | template 288 | static void make_heap(const RandomAccessIterator &first, 289 | const RandomAccessIterator &last, const LessComparer &less_comparer) 290 | { 291 | assert(last >= first); 292 | 293 | typedef typename std::iterator_traits::value_type 294 | value_type; 295 | 296 | const size_t heap_size = last - first; 297 | if (heap_size > 1) { 298 | // Skip leaf nodes without children. This is easy to do for non-paged 299 | // heap, i.e. when page_chunks = 1, but it is difficult for paged heaps. 300 | // So leaf nodes in paged heaps are visited anyway. 301 | size_t i = (PageChunks == 1) ? ((heap_size - 2) / Fanout) : 302 | (heap_size - 2); 303 | do { 304 | value_type item = std::move(first[i]); 305 | _sift_down(first, less_comparer, heap_size, i, item); 306 | } while (i-- > 0); 307 | } 308 | 309 | assert(is_heap(first, last, less_comparer)); 310 | } 311 | 312 | // Makes max heap from items [first ... last) using operator< for items' 313 | // comparison. 314 | template 315 | static void make_heap(const RandomAccessIterator &first, 316 | const RandomAccessIterator &last) 317 | { 318 | make_heap(first, last, _std_less_comparer); 319 | } 320 | 321 | // Pushes the item *(last - 1) into max heap [first ... last - 1) 322 | // using the given less_comparer for items' comparison. 323 | template 324 | static void push_heap(const RandomAccessIterator &first, 325 | const RandomAccessIterator &last, const LessComparer &less_comparer) 326 | { 327 | assert(last > first); 328 | assert(is_heap(first, last - 1, less_comparer)); 329 | 330 | typedef typename std::iterator_traits::value_type 331 | value_type; 332 | 333 | const size_t heap_size = last - first; 334 | if (heap_size > 1) { 335 | const size_t u = heap_size - 1; 336 | value_type item = std::move(first[u]); 337 | _sift_up(first, less_comparer, 0, u, item); 338 | } 339 | 340 | assert(is_heap(first, last, less_comparer)); 341 | } 342 | 343 | // Pushes the item *(last - 1) into max heap [first ... last - 1) 344 | // using operator< for items' comparison. 345 | template 346 | static void push_heap(const RandomAccessIterator &first, 347 | const RandomAccessIterator &last) 348 | { 349 | push_heap(first, last, _std_less_comparer); 350 | } 351 | 352 | // Pops the maximum item from max heap [first ... last) into 353 | // *(last - 1) using the given less_comparer for items' comparison. 354 | template 355 | static void pop_heap(const RandomAccessIterator &first, 356 | const RandomAccessIterator &last, const LessComparer &less_comparer) 357 | { 358 | assert(last > first); 359 | assert(is_heap(first, last, less_comparer)); 360 | 361 | const size_t heap_size = last - first; 362 | if (heap_size > 1) { 363 | _pop_max_item(first, less_comparer, heap_size - 1); 364 | } 365 | 366 | assert(is_heap(first, last - 1, less_comparer)); 367 | } 368 | 369 | // Pops the maximum item from max heap [first ... last) into 370 | // *(last - 1) using operator< for items' comparison. 371 | template 372 | static void pop_heap(const RandomAccessIterator &first, 373 | const RandomAccessIterator &last) 374 | { 375 | pop_heap(first, last, _std_less_comparer); 376 | } 377 | 378 | // Sorts max heap [first ... last) using the given less_comparer 379 | // for items' comparison. 380 | // Items are sorted in ascending order. 381 | template 382 | static void sort_heap(const RandomAccessIterator &first, 383 | const RandomAccessIterator &last, const LessComparer &less_comparer) 384 | { 385 | assert(last >= first); 386 | 387 | const size_t heap_size = last - first; 388 | for (size_t i = heap_size; i > 1; --i) { 389 | _pop_max_item(first, less_comparer, i - 1); 390 | } 391 | } 392 | 393 | // Sorts max heap [first ... last) using operator< for items' comparison. 394 | // Items are sorted in ascending order. 395 | template 396 | static void sort_heap(const RandomAccessIterator &first, 397 | const RandomAccessIterator &last) 398 | { 399 | sort_heap(first, last, _std_less_comparer); 400 | } 401 | 402 | // Swaps the item outside the heap with the maximum item inside 403 | // the heap [first ... last) and restores the heap invariant. 404 | // Uses less_comparer for items' comparisons. 405 | template 406 | static void swap_max_item(const RandomAccessIterator &first, 407 | const RandomAccessIterator &last, 408 | typename std::iterator_traits::value_type &item, 409 | const LessComparer &less_comparer) 410 | { 411 | assert(first < last); 412 | assert(is_heap(first, last, less_comparer)); 413 | 414 | typedef typename std::iterator_traits::value_type 415 | value_type; 416 | 417 | const size_t heap_size = last - first; 418 | 419 | value_type tmp = std::move(item); 420 | _move(item, first[0]); 421 | _sift_down(first, less_comparer, heap_size, 0, tmp); 422 | 423 | assert(is_heap(first, last, less_comparer)); 424 | } 425 | 426 | // Swaps the item outside the heap with the maximum item inside 427 | // the heap [first ... last) and restores the heap invariant. 428 | // Uses operator< for items' comparisons. 429 | template 430 | static void swap_max_item(const RandomAccessIterator &first, 431 | const RandomAccessIterator &last, 432 | typename std::iterator_traits::value_type &item) 433 | { 434 | swap_max_item(first, last, item, _std_less_comparer); 435 | } 436 | 437 | // Restores max heap invariant after item's value has been increased, 438 | // i.e. less_comparer(old_item, new_item) == true. 439 | template 440 | static void restore_heap_after_item_increase( 441 | const RandomAccessIterator &first, const RandomAccessIterator &item, 442 | const LessComparer &less_comparer) 443 | { 444 | assert(item >= first); 445 | assert(is_heap(first, item, less_comparer)); 446 | 447 | typedef typename std::iterator_traits::value_type 448 | value_type; 449 | 450 | const size_t hole_index = item - first; 451 | if (hole_index > 0) { 452 | value_type tmp = std::move(*item); 453 | _sift_up(first, less_comparer, 0, hole_index, tmp); 454 | } 455 | 456 | assert(is_heap(first, item + 1, less_comparer)); 457 | } 458 | 459 | // Restores max heap invariant after item's value has been increased, 460 | // i.e. old_item < new_item. 461 | template 462 | static void restore_heap_after_item_increase( 463 | const RandomAccessIterator &first, const RandomAccessIterator &item) 464 | { 465 | restore_heap_after_item_increase(first, item, 466 | _std_less_comparer); 467 | } 468 | 469 | // Restores max heap invariant after item's value has been decreased, 470 | // i.e. less_comparer(new_item, old_item) == true. 471 | template 472 | static void restore_heap_after_item_decrease( 473 | const RandomAccessIterator &first, const RandomAccessIterator &item, 474 | const RandomAccessIterator &last, const LessComparer &less_comparer) 475 | { 476 | assert(last > first); 477 | assert(item >= first); 478 | assert(item < last); 479 | assert(is_heap(first, item, less_comparer)); 480 | 481 | typedef typename std::iterator_traits::value_type 482 | value_type; 483 | 484 | const size_t heap_size = last - first; 485 | const size_t hole_index = item - first; 486 | value_type tmp = std::move(*item); 487 | _sift_down(first, less_comparer, heap_size, hole_index, tmp); 488 | 489 | assert(is_heap(first, last, less_comparer)); 490 | } 491 | 492 | // Restores max heap invariant after item's value has been decreased, 493 | // i.e. new_item < old_item. 494 | template 495 | static void restore_heap_after_item_decrease( 496 | const RandomAccessIterator &first, const RandomAccessIterator &item, 497 | const RandomAccessIterator &last) 498 | { 499 | restore_heap_after_item_decrease(first, item, last, 500 | _std_less_comparer); 501 | } 502 | 503 | // Removes the given item from the heap and puts it into *(last - 1). 504 | // less_comparer is used for items' comparison. 505 | template 506 | static void remove_from_heap(const RandomAccessIterator &first, 507 | const RandomAccessIterator &item, const RandomAccessIterator &last, 508 | const LessComparer &less_comparer) 509 | { 510 | assert(last > first); 511 | assert(item >= first); 512 | assert(item < last); 513 | assert(is_heap(first, last, less_comparer)); 514 | 515 | typedef typename std::iterator_traits::value_type 516 | value_type; 517 | 518 | const size_t new_heap_size = last - first - 1; 519 | const size_t hole_index = item - first; 520 | if (hole_index < new_heap_size) { 521 | value_type tmp = std::move(first[new_heap_size]); 522 | _move(first[new_heap_size], *item); 523 | if (less_comparer(tmp, first[new_heap_size])) { 524 | _sift_down(first, less_comparer, new_heap_size, hole_index, tmp); 525 | } 526 | else { 527 | _sift_up(first, less_comparer, 0, hole_index, tmp); 528 | } 529 | } 530 | 531 | assert(is_heap(first, last - 1, less_comparer)); 532 | } 533 | 534 | // Removes the given item from the heap and puts it into *(last - 1). 535 | // operator< is used for items' comparison. 536 | template 537 | static void remove_from_heap(const RandomAccessIterator &first, 538 | const RandomAccessIterator &item, const RandomAccessIterator &last) 539 | { 540 | remove_from_heap(first, item, last, 541 | _std_less_comparer); 542 | } 543 | }; 544 | #endif 545 | -------------------------------------------------------------------------------- /gpriority_queue.h: -------------------------------------------------------------------------------- 1 | /* Priority queue on top of gheap. */ 2 | 3 | /****************************************************************************** 4 | * Interface. 5 | *****************************************************************************/ 6 | 7 | #include "gheap.h" 8 | 9 | #include /* for size_t */ 10 | 11 | 12 | /* 13 | * Deletes the given item. 14 | */ 15 | typedef void (*gpriority_queue_item_deleter_t)(void *); 16 | 17 | /* 18 | * Opaque type for priority queue. 19 | */ 20 | struct gpriority_queue; 21 | 22 | /* 23 | * Creates an empty priority queue. 24 | * 25 | * The gheap context pointed by ctx must remain valid until 26 | * gpriority_queue_delete() call. 27 | */ 28 | static inline struct gpriority_queue *gpriority_queue_create( 29 | const struct gheap_ctx *ctx, gpriority_queue_item_deleter_t item_deleter); 30 | 31 | /* 32 | * Creates a pirority queue and copies items from the given array into 33 | * the priority queue. 34 | */ 35 | static inline struct gpriority_queue *gpriority_queue_create_from_array( 36 | const struct gheap_ctx *ctx, gpriority_queue_item_deleter_t item_deleter, 37 | const void *a, size_t n); 38 | 39 | /* 40 | * Deletes the given priority queue. 41 | */ 42 | static inline void gpriority_queue_delete(struct gpriority_queue *q); 43 | 44 | /* 45 | * Returns non-zero if the given priority queue is empty. 46 | * Otherwise returns zero. 47 | */ 48 | static inline int gpriority_queue_empty(struct gpriority_queue *q); 49 | 50 | /* 51 | * Returns the size of the given priority queue. 52 | */ 53 | static inline size_t gpriority_queue_size(struct gpriority_queue *q); 54 | 55 | /* 56 | * Returns a pointer to the top element in the priority queue. 57 | */ 58 | static inline const void *gpriority_queue_top(struct gpriority_queue *q); 59 | 60 | /* 61 | * Pushes a copy of the given item into priority queue. 62 | */ 63 | static inline void gpriority_queue_push(struct gpriority_queue *q, 64 | const void *item); 65 | 66 | /* 67 | * Pops the top element from the priority queue. 68 | */ 69 | static inline void gpriority_queue_pop(struct gpriority_queue *q); 70 | 71 | 72 | /****************************************************************************** 73 | * Implementation. 74 | *****************************************************************************/ 75 | 76 | #include /* for SIZE_MAX */ 77 | #include /* for fprintf() */ 78 | #include /* for malloc(), free() */ 79 | 80 | struct gpriority_queue 81 | { 82 | const struct gheap_ctx *ctx; 83 | gpriority_queue_item_deleter_t item_deleter; 84 | 85 | void *base; 86 | size_t size; 87 | size_t capacity; 88 | }; 89 | 90 | static inline struct gpriority_queue *gpriority_queue_create( 91 | const struct gheap_ctx *const ctx, 92 | const gpriority_queue_item_deleter_t item_deleter) 93 | { 94 | struct gpriority_queue *q = malloc(sizeof(*q)); 95 | 96 | q->ctx = ctx; 97 | q->item_deleter = item_deleter; 98 | 99 | q->base = malloc(ctx->item_size); 100 | q->size = 0; 101 | q->capacity = 1; 102 | 103 | return q; 104 | } 105 | 106 | static inline struct gpriority_queue *gpriority_queue_create_from_array( 107 | const struct gheap_ctx *const ctx, 108 | const gpriority_queue_item_deleter_t item_deleter, 109 | const void *const a, size_t n) 110 | { 111 | struct gpriority_queue *q = malloc(sizeof(*q)); 112 | 113 | q->ctx = ctx; 114 | q->item_deleter = item_deleter; 115 | 116 | assert(n <= SIZE_MAX / ctx->item_size); 117 | q->base = malloc(n * ctx->item_size); 118 | q->size = n; 119 | q->capacity = n; 120 | 121 | for (size_t i = 0; i < n; ++i) { 122 | const void *const src = ((char *)a) + i * ctx->item_size; 123 | void *const dst = ((char *)q->base) + i * ctx->item_size; 124 | ctx->item_mover(dst, src); 125 | } 126 | gheap_make_heap(ctx, q->base, q->size); 127 | 128 | return q; 129 | } 130 | 131 | static inline void gpriority_queue_delete(struct gpriority_queue *const q) 132 | { 133 | for (size_t i = 0; i < q->size; ++i) { 134 | void *const item = ((char *)q->base) + i * q->ctx->item_size; 135 | q->item_deleter(item); 136 | } 137 | free(q->base); 138 | free(q); 139 | } 140 | 141 | static inline int gpriority_queue_empty(struct gpriority_queue *const q) 142 | { 143 | return (q->size == 0); 144 | } 145 | 146 | static inline size_t gpriority_queue_size(struct gpriority_queue *const q) 147 | { 148 | return q->size; 149 | } 150 | 151 | static inline const void *gpriority_queue_top(struct gpriority_queue *const q) 152 | { 153 | assert(q->size > 0); 154 | 155 | return q->base; 156 | } 157 | 158 | static inline void gpriority_queue_push(struct gpriority_queue *const q, 159 | const void *item) 160 | { 161 | if (q->size == q->capacity) { 162 | if (q->capacity > SIZE_MAX / 2 / q->ctx->item_size) { 163 | fprintf(stderr, "priority queue size overflow"); 164 | exit(EXIT_FAILURE); 165 | } 166 | q->capacity *= 2; 167 | char *const new_base = malloc(q->capacity * q->ctx->item_size); 168 | for (size_t i = 0; i < q->size; ++i) { 169 | void *const dst = new_base + i * q->ctx->item_size; 170 | const void *const src = ((char *)q->base) + i * q->ctx->item_size; 171 | q->ctx->item_mover(dst, src); 172 | } 173 | free(q->base); 174 | q->base = new_base; 175 | } 176 | 177 | assert(q->size < q->capacity); 178 | void *const dst = ((char *)q->base) + q->size * q->ctx->item_size; 179 | q->ctx->item_mover(dst, item); 180 | ++(q->size); 181 | gheap_push_heap(q->ctx, q->base, q->size); 182 | } 183 | 184 | static inline void gpriority_queue_pop(struct gpriority_queue *const q) 185 | { 186 | assert(q->size > 0); 187 | 188 | gheap_pop_heap(q->ctx, q->base, q->size); 189 | --(q->size); 190 | void *const item = ((char *)q->base) + q->size * q->ctx->item_size; 191 | q->item_deleter(item); 192 | } 193 | -------------------------------------------------------------------------------- /gpriority_queue.hpp: -------------------------------------------------------------------------------- 1 | // Priority queue on top of Heap. 2 | // 3 | // Pass -DGHEAP_CPP11 to compiler for enabling C++11 optimization, 4 | // otherwise C++03 optimization will be enabled. 5 | 6 | #include 7 | #include // for std::less 8 | #include 9 | 10 | #ifdef GHEAP_CPP11 11 | # include // for std::swap(), std::move(), std::forward() 12 | #else 13 | # include // for std::swap() 14 | #endif 15 | 16 | template , 17 | class LessComparer = std::less > 18 | struct gpriority_queue 19 | { 20 | public: 21 | 22 | typedef Container container_type; 23 | typedef typename Container::value_type value_type; 24 | typedef typename Container::size_type size_type; 25 | typedef typename Container::reference reference; 26 | typedef typename Container::const_reference const_reference; 27 | 28 | LessComparer comp; 29 | Container c; 30 | 31 | private: 32 | 33 | void _make_heap() 34 | { 35 | Heap::make_heap(c.begin(), c.end(), comp); 36 | } 37 | 38 | void _push_heap() 39 | { 40 | Heap::push_heap(c.begin(), c.end(), comp); 41 | } 42 | 43 | void _pop_heap() 44 | { 45 | Heap::pop_heap(c.begin(), c.end(), comp); 46 | } 47 | 48 | public: 49 | explicit gpriority_queue( 50 | const LessComparer &less_comparer = LessComparer(), 51 | const Container &container = Container()) : 52 | comp(less_comparer), c(container) 53 | { 54 | _make_heap(); 55 | } 56 | 57 | template 58 | gpriority_queue(const InputIterator &first, const InputIterator &last, 59 | const LessComparer &less_comparer = LessComparer(), 60 | const Container &container = Container()) : 61 | comp(less_comparer), c(container) 62 | { 63 | c.insert(c.end(), first, last); 64 | _make_heap(); 65 | } 66 | 67 | bool empty() const 68 | { 69 | return c.empty(); 70 | } 71 | 72 | size_type size() const 73 | { 74 | return c.size(); 75 | } 76 | 77 | const_reference top() const 78 | { 79 | assert(!empty()); 80 | 81 | return c.front(); 82 | } 83 | 84 | void push(const T &v) 85 | { 86 | c.push_back(v); 87 | _push_heap(); 88 | } 89 | 90 | void pop() 91 | { 92 | assert(!empty()); 93 | 94 | _pop_heap(); 95 | c.pop_back(); 96 | } 97 | 98 | void swap(gpriority_queue &q) 99 | { 100 | std::swap(c, q.c); 101 | std::swap(comp, q.comp); 102 | } 103 | 104 | #ifdef GHEAP_CPP11 105 | void push(T &&v) 106 | { 107 | c.push_back(std::move(v)); 108 | _push_heap(); 109 | } 110 | #endif 111 | 112 | // Copy constructors and assignment operators are implicitly defined. 113 | }; 114 | 115 | namespace std 116 | { 117 | template 118 | void swap( 119 | gpriority_queue &a, 120 | gpriority_queue &b) 121 | { 122 | a.swap(b); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /ops_count_test.cpp: -------------------------------------------------------------------------------- 1 | // Compares the number of operations with items in gheap-based algorithms 2 | // to the number of operations with items in the corresponding STL algorithms. 3 | // 4 | // Pass -DNDEBUG for eliminating operations related to debugging checks. 5 | 6 | #include "galgorithm.hpp" 7 | #include "gheap.hpp" 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include // for uintptr_t ( is missing in C++03). 17 | #include 18 | 19 | using namespace std; 20 | 21 | // Simulates a LRU list of pages, which can contain up to _max_lru_size entries. 22 | // Each page's size is PAGE_MASK + 1 bytes. 23 | struct lru 24 | { 25 | typedef list lru_t; 26 | 27 | static const uintptr_t PAGE_MASK = (((uintptr_t)1) << 12) - 1; 28 | 29 | // Maximum number of pages in LRU list. 30 | static const size_t MAX_LRU_SIZE = 20; 31 | 32 | // LRU list of pages. Front of the list contains least recently used pages. 33 | static lru_t lru_pages; 34 | 35 | // The number of simulated pagefaults since the last lru::init() call. 36 | static int pagefaults; 37 | 38 | // Resets the model to initial state. 39 | static void reset() 40 | { 41 | lru_pages.clear(); 42 | pagefaults = 0; 43 | } 44 | 45 | // Simulates access to a memory pointed by ptr. 46 | // Brings the accessed page to the back of LRU list. 47 | // If the page is absent in LRU list, then increments pagefaults counter. 48 | static void access_ptr(const void *const ptr) 49 | { 50 | assert(lru_pages.size() <= MAX_LRU_SIZE); 51 | 52 | const uintptr_t page_num = ((uintptr_t)ptr) & ~PAGE_MASK; 53 | lru_t::iterator it = find(lru_pages.begin(), lru_pages.end(), 54 | page_num); 55 | if (it == lru_pages.end()) { 56 | const uintptr_t prev_page_num = page_num - PAGE_MASK - 1; 57 | if (count(lru_pages.begin(), lru_pages.end(), prev_page_num) == 0) { 58 | // Count pagefault only if the previous page is not in the LRU list. 59 | // If the previous page is in the LRU list, then assume that the current 60 | // page is already pre-fetched, so no hard pagefault. 61 | ++pagefaults; 62 | } 63 | 64 | lru_pages.push_front(page_num); 65 | if (lru_pages.size() > MAX_LRU_SIZE) { 66 | lru_pages.pop_back(); 67 | } 68 | assert(lru_pages.size() <= MAX_LRU_SIZE); 69 | } 70 | else { 71 | lru_pages.splice(lru_pages.begin(), lru_pages, it); 72 | } 73 | } 74 | }; 75 | 76 | lru::lru_t lru::lru_pages; 77 | int lru::pagefaults = 0; 78 | 79 | 80 | struct A 81 | { 82 | static int default_ctors; 83 | static int copy_ctors; 84 | static int copy_assignments; 85 | static int swaps; 86 | static int cheap_dtors; 87 | static int expensive_dtors; 88 | static int move_ctors; 89 | static int cheap_move_assignments; 90 | static int expensive_move_assignments; 91 | static int comparisons; 92 | 93 | static void reset() 94 | { 95 | default_ctors = 0; 96 | copy_ctors = 0; 97 | copy_assignments = 0; 98 | swaps = 0; 99 | cheap_dtors = 0; 100 | expensive_dtors = 0; 101 | move_ctors = 0; 102 | cheap_move_assignments = 0; 103 | expensive_move_assignments = 0; 104 | comparisons = 0; 105 | lru::reset(); 106 | } 107 | 108 | static void print() 109 | { 110 | cout << "default_ctors=" << default_ctors << ", copy_ctors=" << 111 | copy_ctors << ", copy_assignments=" << copy_assignments << 112 | ", swaps=" << swaps << ", cheap_dtors=" << cheap_dtors << 113 | ", expensive_dtors=" << expensive_dtors << ", move_ctors=" << 114 | move_ctors << ", cheap_move_assignments=" << cheap_move_assignments << 115 | ", expensive_move_assignments=" << expensive_move_assignments << 116 | ", comparisons=" << comparisons << ", pagefaults=" << lru::pagefaults << 117 | endl; 118 | } 119 | 120 | int value; 121 | 122 | bool has_value() const 123 | { 124 | return (value >= 0); 125 | } 126 | 127 | int get_value() const 128 | { 129 | assert(has_value()); 130 | lru::access_ptr(this); 131 | return value; 132 | } 133 | 134 | void set_value(const int v) 135 | { 136 | assert(v >= 0); 137 | value = v; 138 | lru::access_ptr(this); 139 | } 140 | 141 | void set_value(const A &a) 142 | { 143 | int v = a.get_value(); 144 | set_value(v); 145 | } 146 | 147 | void clear_value() 148 | { 149 | value = -1; 150 | lru::access_ptr(this); 151 | } 152 | 153 | A() 154 | { 155 | ++default_ctors; 156 | clear_value(); 157 | } 158 | 159 | A(const int v) 160 | { 161 | set_value(v); 162 | } 163 | 164 | A(const A &a) 165 | { 166 | ++copy_ctors; 167 | set_value(a); 168 | } 169 | 170 | void operator = (const A &a) 171 | { 172 | if (this == &a) { 173 | return; 174 | } 175 | 176 | assert(has_value()); 177 | ++copy_assignments; 178 | set_value(a); 179 | } 180 | 181 | ~A() 182 | { 183 | if (has_value()) { 184 | ++expensive_dtors; 185 | } 186 | else { 187 | ++cheap_dtors; 188 | } 189 | clear_value(); 190 | } 191 | 192 | #ifdef GHEAP_CPP11 193 | 194 | A(A &&a) 195 | { 196 | ++move_ctors; 197 | set_value(a); 198 | a.clear_value(); 199 | } 200 | 201 | void operator = (A &&a) 202 | { 203 | if (this == &a) { 204 | return; 205 | } 206 | 207 | if (has_value()) { 208 | ++expensive_move_assignments; 209 | } 210 | else { 211 | ++cheap_move_assignments; 212 | } 213 | 214 | set_value(a); 215 | a.clear_value(); 216 | } 217 | 218 | #endif 219 | }; 220 | 221 | int A::default_ctors; 222 | int A::copy_ctors; 223 | int A::copy_assignments; 224 | int A::swaps; 225 | int A::cheap_dtors; 226 | int A::expensive_dtors; 227 | int A::move_ctors; 228 | int A::cheap_move_assignments; 229 | int A::expensive_move_assignments; 230 | int A::comparisons; 231 | 232 | namespace std 233 | { 234 | template <> 235 | void swap(A &a, A &b) 236 | { 237 | ++A::swaps; 238 | 239 | int tmp = a.get_value(); 240 | a.set_value(b); 241 | b.set_value(tmp); 242 | } 243 | } 244 | 245 | bool operator < (const A &a, const A &b) 246 | { 247 | ++A::comparisons; 248 | return (a.get_value() < b.get_value()); 249 | } 250 | 251 | struct stl 252 | { 253 | static const char *name() 254 | { 255 | return "stl"; 256 | } 257 | 258 | template 259 | static void push_heap(const RandomAccessIterator &first, 260 | const RandomAccessIterator &last) 261 | { 262 | ::std::push_heap(first, last); 263 | } 264 | 265 | template 266 | static void pop_heap(const RandomAccessIterator &first, 267 | const RandomAccessIterator &last) 268 | { 269 | ::std::pop_heap(first, last); 270 | } 271 | 272 | template 273 | static void make_heap(const RandomAccessIterator &first, 274 | const RandomAccessIterator &last) 275 | { 276 | ::std::make_heap(first, last); 277 | } 278 | 279 | template 280 | static void sort_heap(const RandomAccessIterator &first, 281 | const RandomAccessIterator &last) 282 | { 283 | ::std::sort_heap(first, last); 284 | } 285 | }; 286 | 287 | struct gtl 288 | { 289 | typedef gheap<> heap; 290 | 291 | static const char *name() 292 | { 293 | return "gheap<>"; 294 | } 295 | 296 | template 297 | static void push_heap(const RandomAccessIterator &first, 298 | const RandomAccessIterator &last) 299 | { 300 | heap::push_heap(first, last); 301 | } 302 | 303 | template 304 | static void pop_heap(const RandomAccessIterator &first, 305 | const RandomAccessIterator &last) 306 | { 307 | heap::pop_heap(first, last); 308 | } 309 | 310 | template 311 | static void make_heap(const RandomAccessIterator &first, 312 | const RandomAccessIterator &last) 313 | { 314 | heap::make_heap(first, last); 315 | } 316 | 317 | template 318 | static void sort_heap(const RandomAccessIterator &first, 319 | const RandomAccessIterator &last) 320 | { 321 | heap::sort_heap(first, last); 322 | } 323 | }; 324 | 325 | namespace { 326 | 327 | void init_array(vector &a, const size_t n) 328 | { 329 | a.clear(); 330 | srand(0); 331 | generate_n(back_inserter(a), n, rand); 332 | } 333 | 334 | template 335 | void test_push_heap(vector &a, const size_t n) 336 | { 337 | cout << " test_push_heap(" << Heap::name() << "): "; 338 | 339 | init_array(a, n); 340 | A::reset(); 341 | for (size_t i = 2; i <= n; ++i) { 342 | Heap::push_heap(a.begin(), a.begin() + i); 343 | } 344 | A::print(); 345 | } 346 | 347 | template 348 | void test_pop_heap(vector &a, const size_t n) 349 | { 350 | cout << " test_pop_heap(" << Heap::name() << "): "; 351 | 352 | init_array(a, n); 353 | Heap::make_heap(a.begin(), a.end()); 354 | 355 | A::reset(); 356 | for (size_t i = 0; i < n - 1; ++i) { 357 | Heap::pop_heap(a.begin(), a.end() - i); 358 | } 359 | A::print(); 360 | } 361 | 362 | template 363 | void test_make_heap(vector &a, const size_t n) 364 | { 365 | cout << " test_make_heap(" << Heap::name() << "): "; 366 | 367 | init_array(a, n); 368 | A::reset(); 369 | Heap::make_heap(a.begin(), a.end()); 370 | A::print(); 371 | } 372 | 373 | template 374 | void test_sort_heap(vector &a, const size_t n) 375 | { 376 | cout << " test_sort_heap(" << Heap::name() << "): "; 377 | 378 | init_array(a, n); 379 | Heap::make_heap(a.begin(), a.end()); 380 | 381 | A::reset(); 382 | Heap::sort_heap(a.begin(), a.end()); 383 | A::print(); 384 | } 385 | 386 | void test_nway_mergesort_avg(vector &a, const size_t n) 387 | { 388 | cout << " test_nway_mergesort_avg(" << gtl::name() << "): "; 389 | 390 | typedef galgorithm algorithm; 391 | 392 | init_array(a, n); 393 | A::reset(); 394 | algorithm::nway_mergesort(a.begin(), a.end()); 395 | A::print(); 396 | } 397 | 398 | void test_nway_mergesort_worst(vector &a, const size_t n) 399 | { 400 | cout << " test_nway_mergesort_worst(" << gtl::name() << "): "; 401 | 402 | typedef galgorithm algorithm; 403 | 404 | // Simulate worst case for SGI STL sort implementation (aka introsort) - 405 | // see http://en.wikipedia.org/wiki/Introsort . 406 | // Actually n-way mergesort must be free of bad cases. 407 | for (size_t i = 0; i < n; ++i) { 408 | a[i] = n - i; 409 | } 410 | 411 | init_array(a, n); 412 | A::reset(); 413 | algorithm::nway_mergesort(a.begin(), a.end()); 414 | A::print(); 415 | } 416 | 417 | void test_sort_avg(vector &a, const size_t n) 418 | { 419 | cout << " test_sort_avg(" << stl::name() << "): "; 420 | 421 | init_array(a, n); 422 | A::reset(); 423 | sort(a.begin(), a.end()); 424 | A::print(); 425 | } 426 | 427 | void test_sort_worst(vector &a, const size_t n) 428 | { 429 | cout << " test_sort_worst(" << stl::name() << "): "; 430 | 431 | // Simulate worst case for SGI STL sort implementation (aka introsort) - 432 | // see http://en.wikipedia.org/wiki/Introsort . 433 | for (size_t i = 0; i < n; ++i) { 434 | a[i] = n - i; 435 | } 436 | 437 | A::reset(); 438 | sort(a.begin(), a.end()); 439 | A::print(); 440 | } 441 | 442 | } // end of anonymous namespace 443 | 444 | int main() 445 | { 446 | const size_t N = 1000000; 447 | 448 | cout << "N=" << N << endl; 449 | 450 | vector a; 451 | a.reserve(N); 452 | 453 | test_push_heap(a, N); 454 | test_push_heap(a, N); 455 | 456 | test_pop_heap(a, N); 457 | test_pop_heap(a, N); 458 | 459 | test_make_heap(a, N); 460 | test_make_heap(a, N); 461 | 462 | test_sort_heap(a, N); 463 | test_sort_heap(a, N); 464 | 465 | test_nway_mergesort_avg(a, N); 466 | test_nway_mergesort_worst(a, N); 467 | test_sort_avg(a, N); 468 | test_sort_worst(a, N); 469 | } 470 | -------------------------------------------------------------------------------- /perftests.c: -------------------------------------------------------------------------------- 1 | #include "galgorithm.h" 2 | #include "gheap.h" 3 | #include "gpriority_queue.h" 4 | 5 | #include 6 | #include // for printf() 7 | #include // for rand(), srand() 8 | #include // for clock() 9 | 10 | typedef size_t T; 11 | 12 | static int less(const void *const ctx, const void *const a, const void *const b) 13 | { 14 | (void)ctx; 15 | return *((T *)a) < *((T *)b); 16 | } 17 | 18 | static void move(void *const dst, const void *const src) 19 | { 20 | *((T *)dst) = *((T *)src); 21 | } 22 | 23 | static double get_time(void) 24 | { 25 | return (double)clock() / CLOCKS_PER_SEC; 26 | } 27 | 28 | static void print_performance(const double t, const size_t m) 29 | { 30 | printf(": %.0lf Kops/s\n", m / t / 1000); 31 | } 32 | 33 | static void init_array(T *const a, const size_t n) 34 | { 35 | for (size_t i = 0; i < n; ++i) { 36 | a[i] = rand(); 37 | } 38 | } 39 | 40 | static void perftest_heapsort(const struct gheap_ctx *const ctx, 41 | T *const a, const size_t n, const size_t m) 42 | { 43 | printf("perftest_heapsort(n=%zu, m=%zu)", n, m); 44 | 45 | double total_time = 0; 46 | 47 | for (size_t i = 0; i < m / n; ++i) { 48 | init_array(a, n); 49 | 50 | const double start = get_time(); 51 | galgorithm_heapsort(ctx, a, n); 52 | const double end = get_time(); 53 | 54 | total_time += end - start; 55 | } 56 | 57 | print_performance(total_time, m); 58 | } 59 | 60 | static void perftest_partial_sort(const struct gheap_ctx *const ctx, 61 | T *const a, const size_t n, const size_t m) 62 | { 63 | const size_t k = n / 4; 64 | 65 | printf("perftest_partial_sort(n=%zu, m=%zu, k=%zu)", n, m, k); 66 | 67 | double total_time = 0; 68 | 69 | for (size_t i = 0; i < m / n; ++i) { 70 | init_array(a, n); 71 | 72 | const double start = get_time(); 73 | galgorithm_partial_sort(ctx, a, n, k); 74 | const double end = get_time(); 75 | 76 | total_time += end - start; 77 | } 78 | 79 | print_performance(total_time, m); 80 | } 81 | 82 | static void small_range_sorter(const void *const ctx, void *const a, 83 | const size_t n) 84 | { 85 | galgorithm_heapsort(ctx, a, n); 86 | } 87 | 88 | static void perftest_nway_mergesort(const struct gheap_ctx *const ctx, 89 | T *const a, const size_t n, const size_t m) 90 | { 91 | const size_t small_range_size = ((1 << 20) - 1) / 3; 92 | const size_t subranges_count = 15; 93 | 94 | printf("perftest_nway_mergesort(n=%zu, m=%zu, small_range_size=%zu, " 95 | "subranges_count=%zu)", n, m, small_range_size, subranges_count); 96 | 97 | double total_time = 0; 98 | 99 | struct gheap_ctx small_range_sorter_ctx = *ctx; 100 | small_range_sorter_ctx.fanout = 4; 101 | 102 | for (size_t i = 0; i < m / n; ++i) { 103 | init_array(a, n); 104 | 105 | const double start = get_time(); 106 | T *const items_tmp_buf = malloc(sizeof(items_tmp_buf[0]) * n); 107 | galgorithm_nway_mergesort(ctx, a, n, 108 | &small_range_sorter, &small_range_sorter_ctx, 109 | small_range_size, subranges_count, items_tmp_buf); 110 | free(items_tmp_buf); 111 | const double end = get_time(); 112 | 113 | total_time += end - start; 114 | } 115 | 116 | print_performance(total_time, m); 117 | } 118 | 119 | static void delete_item(void *item) 120 | { 121 | /* do nothing */ 122 | (void)item; 123 | } 124 | 125 | static void perftest_priority_queue(const struct gheap_ctx *const ctx, 126 | T *const a, const size_t n, const size_t m) 127 | { 128 | printf("perftest_priority_queue(n=%zu, m=%zu)", n, m); 129 | 130 | init_array(a, n); 131 | struct gpriority_queue *const q = gpriority_queue_create_from_array( 132 | ctx, &delete_item, a, n); 133 | 134 | double start = get_time(); 135 | for (size_t i = 0; i < m; ++i) { 136 | gpriority_queue_pop(q); 137 | const T tmp = rand(); 138 | gpriority_queue_push(q, &tmp); 139 | } 140 | double end = get_time(); 141 | 142 | gpriority_queue_delete(q); 143 | 144 | print_performance(end - start, m); 145 | } 146 | 147 | static void perftest(const struct gheap_ctx *const ctx, T *const a, 148 | const size_t max_n) 149 | { 150 | size_t n = max_n; 151 | while (n > 0) { 152 | perftest_heapsort(ctx, a, n, max_n); 153 | perftest_partial_sort(ctx, a, n, max_n); 154 | perftest_nway_mergesort(ctx, a, n, max_n); 155 | perftest_priority_queue(ctx, a, n, max_n); 156 | 157 | n >>= 1; 158 | } 159 | } 160 | 161 | static const struct gheap_ctx ctx_v = { 162 | .fanout = 2, 163 | .page_chunks = 1, 164 | .item_size = sizeof(T), 165 | .less_comparer = &less, 166 | .less_comparer_ctx = NULL, 167 | .item_mover = &move, 168 | }; 169 | 170 | int main(void) 171 | { 172 | static const size_t MAX_N = 32 * 1024 * 1024; 173 | 174 | printf("fanout=%zu, page_chunks=%zu, max_n=%zu\n", 175 | ctx_v.fanout, ctx_v.page_chunks, MAX_N); 176 | 177 | srand(0); 178 | T *const a = malloc(sizeof(a[0]) * MAX_N); 179 | 180 | perftest(&ctx_v, a, MAX_N); 181 | 182 | free(a); 183 | 184 | return 0; 185 | } 186 | -------------------------------------------------------------------------------- /perftests.cpp: -------------------------------------------------------------------------------- 1 | // Pass -DGHEAP_CPP11 to compiler for gheap_cpp11.hpp tests, 2 | // otherwise gheap_cpp03.hpp will be tested. 3 | 4 | #include "galgorithm.hpp" 5 | #include "gheap.hpp" 6 | #include "gpriority_queue.hpp" 7 | 8 | #include // for *_heap(), copy() 9 | #include // for rand(), srand() 10 | #include // for clock() 11 | #include 12 | #include // for priority_queue 13 | #include // for pair 14 | #include // for vector 15 | 16 | using namespace std; 17 | 18 | namespace { 19 | 20 | double get_time() 21 | { 22 | return (double)clock() / CLOCKS_PER_SEC; 23 | } 24 | 25 | void print_performance(const double t, const size_t m) 26 | { 27 | cout << ": " << (m / t / 1000) << " Kops/s" << endl; 28 | } 29 | 30 | template 31 | void init_array(T *const a, const size_t n) 32 | { 33 | for (size_t i = 0; i < n; ++i) { 34 | a[i] = rand(); 35 | } 36 | } 37 | 38 | // Dummy wrapper for STL heap. 39 | struct stl_heap 40 | { 41 | template 42 | static void make_heap(const RandomAccessIterator &first, 43 | const RandomAccessIterator &last, const LessComparer &less_comparer) 44 | { 45 | std::make_heap(first, last, less_comparer); 46 | } 47 | 48 | template 49 | static void sort_heap(const RandomAccessIterator &first, 50 | const RandomAccessIterator &last, const LessComparer &less_comparer) 51 | { 52 | std::sort_heap(first, last, less_comparer); 53 | } 54 | }; 55 | 56 | // Dummy wrapper for STL algorithms. 57 | struct stl_algorithm 58 | { 59 | template 60 | static void partial_sort(const RandomAccessIterator &first, 61 | const RandomAccessIterator &middle, const RandomAccessIterator &last) 62 | { 63 | std::partial_sort(first, middle, last); 64 | } 65 | }; 66 | 67 | template 68 | void perftest_heapsort(T *const a, const size_t n, const size_t m) 69 | { 70 | cout << "perftest_heapsort(n=" << n << ", m=" << m << ")"; 71 | 72 | typedef galgorithm algorithm; 73 | 74 | double total_time = 0; 75 | 76 | for (size_t i = 0; i < m / n; ++i) { 77 | init_array(a, n); 78 | 79 | const double start = get_time(); 80 | algorithm::heapsort(a, a + n); 81 | const double end = get_time(); 82 | 83 | total_time += end - start; 84 | } 85 | 86 | print_performance(total_time, m); 87 | } 88 | 89 | template 90 | void perftest_partial_sort(T *const a, const size_t n, const size_t m) 91 | { 92 | const size_t k = n / 4; 93 | 94 | cout << "perftest_partial_sort(n=" << n << ", m=" << m << ", k=" << k << ")"; 95 | 96 | double total_time = 0; 97 | 98 | for (size_t i = 0; i < m / n; ++i) { 99 | init_array(a, n); 100 | 101 | const double start = get_time(); 102 | Algorithm::partial_sort(a, a + k, a + n); 103 | const double end = get_time(); 104 | 105 | total_time += end - start; 106 | } 107 | 108 | print_performance(total_time, m); 109 | } 110 | 111 | template 112 | bool less_comparer(const T &a, const T &b) 113 | { 114 | return (a < b); 115 | } 116 | 117 | template 118 | void small_range_sorter(T *const first, T *const last, 119 | bool (&less_comparer)(const T &, const T &)) 120 | { 121 | galgorithm >::heapsort(first, last, less_comparer); 122 | } 123 | 124 | template 125 | void perftest_nway_mergesort(T *const a, const size_t n, const size_t m) 126 | { 127 | const size_t small_range_size = (1 << 15) - 1; 128 | const size_t subranges_count = 7; 129 | 130 | cout << "perftest_nway_mergesort(n=" << n << ", m=" << m << 131 | ", small_range_size=" << small_range_size << ", subranges_count=" << 132 | subranges_count << ")"; 133 | 134 | typedef galgorithm algorithm; 135 | 136 | double total_time = 0; 137 | 138 | for (size_t i = 0; i < m / n; ++i) { 139 | init_array(a, n); 140 | 141 | const double start = get_time(); 142 | algorithm::nway_mergesort(a, a + n, 143 | less_comparer, small_range_sorter, 144 | small_range_size, subranges_count); 145 | const double end = get_time(); 146 | 147 | total_time += end - start; 148 | } 149 | 150 | print_performance(total_time, m); 151 | } 152 | 153 | template 154 | void perftest_priority_queue(T *const a, const size_t n, const size_t m) 155 | { 156 | cout << "perftest_priority_queue(n=" << n << ", m=" << m << ")"; 157 | 158 | init_array(a, n); 159 | PriorityQueue q(a, a + n); 160 | 161 | const double start = get_time(); 162 | for (size_t i = 0; i < m; ++i) { 163 | q.pop(); 164 | q.push(rand()); 165 | } 166 | const double end = get_time(); 167 | 168 | print_performance(end - start, m); 169 | } 170 | 171 | template 172 | void perftest_gheap(T *const a, const size_t max_n) 173 | { 174 | size_t n = max_n; 175 | while (n > 0) { 176 | perftest_heapsort(a, n, max_n); 177 | perftest_partial_sort >(a, n, max_n); 178 | perftest_nway_mergesort(a, n, max_n); 179 | perftest_priority_queue >(a, n, max_n); 180 | 181 | n >>= 1; 182 | } 183 | } 184 | 185 | template 186 | void perftest_stl_heap(T *const a, const size_t max_n) 187 | { 188 | size_t n = max_n; 189 | while (n > 0) { 190 | perftest_heapsort(a, n, max_n); 191 | perftest_partial_sort(a, n, max_n); 192 | 193 | // stl heap doesn't provide nway_merge(), 194 | // so skip perftest_nway_mergesort(). 195 | 196 | perftest_priority_queue >(a, n, max_n); 197 | 198 | n >>= 1; 199 | } 200 | } 201 | 202 | } // end of anonymous namespace. 203 | 204 | 205 | int main(void) 206 | { 207 | static const size_t MAX_N = 32 * 1024 * 1024; 208 | static const size_t FANOUT = 2; 209 | static const size_t PAGE_CHUNKS = 1; 210 | typedef size_t T; 211 | 212 | cout << "fanout=" << FANOUT << ", page_chunks=" << PAGE_CHUNKS << 213 | ", max_n=" << MAX_N << endl; 214 | 215 | srand(0); 216 | T *const a = new T[MAX_N]; 217 | 218 | cout << "* STL heap" << endl; 219 | perftest_stl_heap(a, MAX_N); 220 | 221 | cout << "* gheap" << endl; 222 | typedef gheap heap; 223 | perftest_gheap(a, MAX_N); 224 | 225 | delete[] a; 226 | } 227 | -------------------------------------------------------------------------------- /tests.c: -------------------------------------------------------------------------------- 1 | /* Tests for C99 gheap, galgorithm and gpriority_queue */ 2 | 3 | #include "galgorithm.h" 4 | #include "gheap.h" 5 | #include "gpriority_queue.h" 6 | 7 | #include 8 | #include /* for uintptr_t, SIZE_MAX */ 9 | #include /* for printf() */ 10 | #include /* for srand(), rand(), malloc(), free() */ 11 | 12 | static int less_comparer(const void *const ctx, const void *const a, 13 | const void *const b) 14 | { 15 | uintptr_t must_invert = (uintptr_t)ctx; 16 | return (must_invert ? (*(int *)b < *(int *)a): (*(int *)a < *(int *)b)); 17 | } 18 | 19 | static void item_mover(void *const dst, const void *const src) 20 | { 21 | *((int *)dst) = *((int *)src); 22 | } 23 | 24 | static void test_parent_child(const struct gheap_ctx *const ctx, 25 | const size_t start_index, const size_t n) 26 | { 27 | assert(start_index > 0); 28 | assert(start_index <= SIZE_MAX - n); 29 | 30 | printf(" test_parent_child(start_index=%zu, n=%zu) ", start_index, n); 31 | 32 | const size_t fanout = ctx->fanout; 33 | 34 | for (size_t i = 0; i < n; ++i) { 35 | const size_t u = start_index + i; 36 | size_t v = gheap_get_child_index(ctx, u); 37 | if (v < SIZE_MAX) { 38 | assert(v > u); 39 | v = gheap_get_parent_index(ctx, v); 40 | assert(v == u); 41 | } 42 | 43 | v = gheap_get_parent_index(ctx, u); 44 | assert(v < u); 45 | v = gheap_get_child_index(ctx, v); 46 | assert(v <= u && u - v < fanout); 47 | } 48 | 49 | printf("OK\n"); 50 | } 51 | 52 | static void test_is_heap(const struct gheap_ctx *const ctx, 53 | const size_t n, int *const a) 54 | { 55 | assert(n > 0); 56 | 57 | printf(" test_is_heap(n=%zu) ", n); 58 | 59 | /* Verify that ascending sorted array creates one-item heap. */ 60 | for (size_t i = 0; i < n; ++i) { 61 | a[i] = i; 62 | } 63 | assert(gheap_is_heap_until(ctx, a, n) == 1); 64 | assert(gheap_is_heap(ctx, a, 1)); 65 | if (n > 1) { 66 | assert(!gheap_is_heap(ctx, a, n)); 67 | } 68 | 69 | /* Verify that descending sorted array creates valid heap. */ 70 | for (size_t i = 0; i < n; ++i) { 71 | a[i] = n - i; 72 | } 73 | assert(gheap_is_heap_until(ctx, a, n) == n); 74 | assert(gheap_is_heap(ctx, a, n)); 75 | 76 | /* Verify that array containing identical items creates valid heap. */ 77 | for (size_t i = 0; i < n; ++i) { 78 | a[i] = n; 79 | } 80 | assert(gheap_is_heap_until(ctx, a, n) == n); 81 | assert(gheap_is_heap(ctx, a, n)); 82 | 83 | printf("OK\n"); 84 | } 85 | 86 | static void init_array(int *const a, const size_t n) 87 | { 88 | for (size_t i = 0; i < n; ++i) { 89 | a[i] = rand(); 90 | } 91 | } 92 | 93 | static void assert_sorted(const struct gheap_ctx *const ctx, 94 | const int *const base, const size_t n) 95 | { 96 | const gheap_less_comparer_t less_comparer = ctx->less_comparer; 97 | const void *const less_comparer_ctx = ctx->less_comparer_ctx; 98 | 99 | for (size_t i = 1; i < n; ++i) { 100 | assert(!less_comparer(less_comparer_ctx, &base[i], &base[i - 1])); 101 | } 102 | } 103 | 104 | static void test_make_heap(const struct gheap_ctx *const ctx, 105 | const size_t n, int *const a) 106 | { 107 | printf(" test_make_heap(n=%zu) ", n); 108 | 109 | init_array(a, n); 110 | gheap_make_heap(ctx, a, n); 111 | assert(gheap_is_heap(ctx, a, n)); 112 | 113 | printf("OK\n"); 114 | } 115 | 116 | static void test_sort_heap(const struct gheap_ctx *const ctx, 117 | const size_t n, int *const a) 118 | { 119 | printf(" test_sort_heap(n=%zu) ", n); 120 | 121 | /* Verify ascending sorting. */ 122 | init_array(a, n); 123 | gheap_make_heap(ctx, a, n); 124 | gheap_sort_heap(ctx, a, n); 125 | assert_sorted(ctx, a, n); 126 | 127 | /* Verify descending sorting. */ 128 | const struct gheap_ctx ctx_desc = { 129 | .fanout = ctx->fanout, 130 | .page_chunks = ctx->page_chunks, 131 | .item_size = ctx->item_size, 132 | .less_comparer = &less_comparer, 133 | .less_comparer_ctx = (void *)1, 134 | .item_mover = ctx->item_mover, 135 | }; 136 | 137 | init_array(a, n); 138 | gheap_make_heap(&ctx_desc, a, n); 139 | gheap_sort_heap(&ctx_desc, a, n); 140 | assert_sorted(&ctx_desc, a, n); 141 | 142 | printf("OK\n"); 143 | } 144 | 145 | static void test_push_heap(const struct gheap_ctx *const ctx, 146 | const size_t n, int *const a) 147 | { 148 | printf(" test_push_heap(n=%zu) ", n); 149 | 150 | init_array(a, n); 151 | 152 | for (size_t i = 0; i < n; ++i) { 153 | gheap_push_heap(ctx, a, i + 1); 154 | } 155 | assert(gheap_is_heap(ctx, a, n)); 156 | 157 | printf("OK\n"); 158 | } 159 | 160 | static void test_pop_heap(const struct gheap_ctx *const ctx, 161 | const size_t n, int *const a) 162 | { 163 | printf(" test_pop_heap(n=%zu) ", n); 164 | 165 | init_array(a, n); 166 | 167 | gheap_make_heap(ctx, a, n); 168 | for (size_t i = 0; i < n; ++i) { 169 | const int item = a[0]; 170 | gheap_pop_heap(ctx, a, n - i); 171 | assert(item == a[n - i - 1]); 172 | } 173 | assert_sorted(ctx, a, n); 174 | 175 | printf("OK\n"); 176 | } 177 | 178 | static void test_swap_max_item(const struct gheap_ctx *const ctx, 179 | const size_t n, int *const a) 180 | { 181 | printf(" test_swap_max_item(n=%zu) ", n); 182 | 183 | init_array(a, n); 184 | 185 | const size_t m = n / 2; 186 | if (m > 0) { 187 | gheap_make_heap(ctx, a, m); 188 | for (size_t i = m; i < n; ++i) { 189 | const int max_item = a[0]; 190 | gheap_swap_max_item(ctx, a, m, &a[i]); 191 | assert(max_item == a[i]); 192 | assert(gheap_is_heap(ctx, a, m)); 193 | } 194 | } 195 | 196 | printf("OK\n"); 197 | } 198 | 199 | static void test_restore_heap_after_item_increase( 200 | const struct gheap_ctx *const ctx, 201 | const size_t n, int *const a) 202 | { 203 | printf(" test_restore_heap_after_item_increase(n=%zu) ", n); 204 | 205 | init_array(a, n); 206 | 207 | gheap_make_heap(ctx, a, n); 208 | for (size_t i = 0; i < n; ++i) { 209 | const size_t item_index = rand() % n; 210 | const int old_item = a[item_index]; 211 | 212 | /* Don't allow integer overflow. */ 213 | size_t fade = SIZE_MAX; 214 | do { 215 | /* Division by zero is impossible here. */ 216 | a[item_index] = old_item + rand() % fade; 217 | fade /= 2; 218 | } while (a[item_index] < old_item); 219 | gheap_restore_heap_after_item_increase(ctx, a, n, item_index); 220 | assert(gheap_is_heap(ctx, a, n)); 221 | } 222 | 223 | printf("OK\n"); 224 | } 225 | 226 | static void test_restore_heap_after_item_decrease( 227 | const struct gheap_ctx *const ctx, 228 | const size_t n, int *const a) 229 | { 230 | printf(" test_restore_heap_after_item_decrease(n=%zu) ", n); 231 | 232 | init_array(a, n); 233 | 234 | gheap_make_heap(ctx, a, n); 235 | for (size_t i = 0; i < n; ++i) { 236 | const size_t item_index = rand() % n; 237 | const int old_item = a[item_index]; 238 | 239 | /* Don't allow integer underflow. */ 240 | size_t fade = SIZE_MAX; 241 | do { 242 | /* Division by zero is impossible here. */ 243 | a[item_index] = old_item - rand() % fade; 244 | fade /= 2; 245 | } while (a[item_index] > old_item); 246 | gheap_restore_heap_after_item_decrease(ctx, a, n, item_index); 247 | assert(gheap_is_heap(ctx, a, n)); 248 | } 249 | 250 | printf("OK\n"); 251 | } 252 | 253 | static void test_remove_from_heap(const struct gheap_ctx *const ctx, 254 | const size_t n, int *const a) 255 | { 256 | printf(" test_remove_from_heap(n=%zu) ", n); 257 | 258 | init_array(a, n); 259 | 260 | gheap_make_heap(ctx, a, n); 261 | for (size_t i = 0; i < n; ++i) { 262 | const size_t item_index = rand() % (n - i); 263 | const int item = a[item_index]; 264 | gheap_remove_from_heap(ctx, a, n - i, item_index); 265 | assert(gheap_is_heap(ctx, a, n - i - 1)); 266 | assert(item == a[n - i - 1]); 267 | } 268 | 269 | printf("OK\n"); 270 | } 271 | 272 | static const int *min_element(const int *const a, const size_t n) 273 | { 274 | assert(n > 0); 275 | const int *min_ptr = a; 276 | for (size_t i = 1; i < n; ++i) { 277 | if (a[i] < *min_ptr) { 278 | min_ptr = a + i; 279 | } 280 | } 281 | return min_ptr; 282 | } 283 | 284 | static void test_heapsort(const struct gheap_ctx *const ctx, 285 | const size_t n, int *const a) 286 | { 287 | printf(" test_heapsort(n=%zu) ", n); 288 | 289 | /* Verify ascending sorting. */ 290 | init_array(a, n); 291 | galgorithm_heapsort(ctx, a, n); 292 | assert_sorted(ctx, a, n); 293 | 294 | /* Verify descending sorting. */ 295 | const struct gheap_ctx ctx_desc = { 296 | .fanout = ctx->fanout, 297 | .page_chunks = ctx->page_chunks, 298 | .item_size = ctx->item_size, 299 | .less_comparer = &less_comparer, 300 | .less_comparer_ctx = (void *)1, 301 | .item_mover = ctx->item_mover, 302 | }; 303 | 304 | init_array(a, n); 305 | galgorithm_heapsort(&ctx_desc, a, n); 306 | assert_sorted(&ctx_desc, a, n); 307 | 308 | printf("OK\n"); 309 | } 310 | 311 | static void test_partial_sort(const struct gheap_ctx *const ctx, 312 | const size_t n, int *const a) 313 | { 314 | printf(" test_partial_sort(n=%zu) ", n); 315 | 316 | // Check 0-items partial sort. 317 | init_array(a, n); 318 | galgorithm_partial_sort(ctx, a, n, 0); 319 | 320 | // Check 1-item partial sort. 321 | if (n > 0) { 322 | init_array(a, n); 323 | galgorithm_partial_sort(ctx, a, n, 1); 324 | assert(min_element(a, n) == a); 325 | } 326 | 327 | // Check 2-items partial sort. 328 | if (n > 1) { 329 | init_array(a, n); 330 | galgorithm_partial_sort(ctx, a, n, 2); 331 | assert_sorted(ctx, a, 2); 332 | assert(min_element(a + 1, n - 1) == a + 1); 333 | } 334 | 335 | // Check n-items partial sort. 336 | init_array(a, n); 337 | galgorithm_partial_sort(ctx, a, n, n); 338 | assert_sorted(ctx, a, n); 339 | 340 | // Check (n-1)-items partial sort. 341 | if (n > 0) { 342 | init_array(a, n); 343 | galgorithm_partial_sort(ctx, a, n, n - 1); 344 | assert_sorted(ctx, a, n); 345 | } 346 | 347 | // Check (n-2)-items partial sort. 348 | if (n > 2) { 349 | init_array(a, n); 350 | galgorithm_partial_sort(ctx, a, n, n - 2); 351 | assert_sorted(ctx, a, n - 2); 352 | assert(min_element(a + n - 3, 3) == a + n - 3); 353 | } 354 | 355 | printf("OK\n"); 356 | } 357 | 358 | struct nway_merge_input_ctx 359 | { 360 | int *next; 361 | int *end; 362 | }; 363 | 364 | static int nway_merge_input_next(void *const ctx) 365 | { 366 | struct nway_merge_input_ctx *const c = ctx; 367 | assert(c->next <= c->end); 368 | if (c->next < c->end) { 369 | ++(c->next); 370 | } 371 | return (c->next < c->end); 372 | } 373 | 374 | static const void *nway_merge_input_get(const void *const ctx) 375 | { 376 | const struct nway_merge_input_ctx *const c = ctx; 377 | assert(c->next < c->end); 378 | return c->next; 379 | } 380 | 381 | static void nway_ctx_mover(void *const dst, const void *const src) 382 | { 383 | *(struct nway_merge_input_ctx *)dst = *(struct nway_merge_input_ctx *)src; 384 | } 385 | 386 | static const struct galgorithm_nway_merge_input_vtable 387 | nway_merge_input_vtable = { 388 | .next = &nway_merge_input_next, 389 | .get = &nway_merge_input_get, 390 | }; 391 | 392 | struct nway_merge_output_ctx 393 | { 394 | int *next; 395 | }; 396 | 397 | static void nway_merge_output_put(void *const ctx, const void *const data) 398 | { 399 | struct nway_merge_output_ctx *const c = ctx; 400 | item_mover(c->next, data); 401 | ++(c->next); 402 | } 403 | 404 | static const struct galgorithm_nway_merge_output_vtable 405 | nway_merge_output_vtable = { 406 | .put = &nway_merge_output_put, 407 | }; 408 | 409 | static void small_range_sorter(const void *const ctx, 410 | void *const a, const size_t n) 411 | { 412 | galgorithm_heapsort(ctx, a, n); 413 | } 414 | 415 | static void test_nway_mergesort(const struct gheap_ctx *const ctx, 416 | const size_t n, int *const a) 417 | { 418 | printf(" test_nway_mergesort(n=%zu) ", n); 419 | 420 | int *const items_tmp_buf = malloc(sizeof(a[0]) * n); 421 | 422 | // Verify 2-way mergesort with small_range_size = 1. 423 | init_array(a, n); 424 | galgorithm_nway_mergesort(ctx, a, n, &small_range_sorter, ctx, 1, 2, 425 | items_tmp_buf); 426 | assert_sorted(ctx, a, n); 427 | 428 | // Verify 3-way mergesort with small_range_size = 2. 429 | init_array(a, n); 430 | galgorithm_nway_mergesort(ctx, a, n, &small_range_sorter, ctx, 2, 3, 431 | items_tmp_buf); 432 | assert_sorted(ctx, a, n); 433 | 434 | // Verify 10-way mergesort with small_range_size = 9. 435 | init_array(a, n); 436 | galgorithm_nway_mergesort(ctx, a, n, &small_range_sorter, ctx, 10, 9, 437 | items_tmp_buf); 438 | assert_sorted(ctx, a, n); 439 | 440 | free(items_tmp_buf); 441 | 442 | printf("OK\n"); 443 | } 444 | 445 | static void test_nway_merge(const struct gheap_ctx *const ctx, 446 | const size_t n, int *const a) 447 | { 448 | printf(" test_nway_merge(n=%zu) ", n); 449 | 450 | int *const b = malloc(sizeof(*b) * n); 451 | 452 | struct galgorithm_nway_merge_input input = { 453 | .vtable = &nway_merge_input_vtable, 454 | .ctxs = NULL, 455 | .ctxs_count = 0, 456 | .ctx_size = sizeof(struct nway_merge_input_ctx), 457 | .ctx_mover = &nway_ctx_mover, 458 | }; 459 | 460 | struct nway_merge_output_ctx out_ctx; 461 | 462 | const struct galgorithm_nway_merge_output output = { 463 | .vtable = &nway_merge_output_vtable, 464 | .ctx = &out_ctx, 465 | }; 466 | 467 | // Check 1-way merge. 468 | init_array(a, n); 469 | galgorithm_heapsort(ctx, a, n); 470 | 471 | struct nway_merge_input_ctx one_way_input_ctxs[1] = { 472 | { 473 | .next = a, 474 | .end = a + n, 475 | }, 476 | }; 477 | 478 | input.ctxs = one_way_input_ctxs; 479 | input.ctxs_count = 1; 480 | out_ctx.next = b; 481 | galgorithm_nway_merge(ctx, &input, &output); 482 | assert_sorted(ctx, b, n); 483 | 484 | // Check 2-way merge. 485 | if (n > 1) { 486 | init_array(a, n); 487 | galgorithm_heapsort(ctx, a, n / 2); 488 | galgorithm_heapsort(ctx, a + n / 2, n - n / 2); 489 | 490 | struct nway_merge_input_ctx two_way_input_ctxs[2] = { 491 | { 492 | .next = a, 493 | .end = a + n / 2, 494 | }, 495 | { 496 | .next = a + n / 2, 497 | .end = a + n, 498 | }, 499 | }; 500 | 501 | input.ctxs = two_way_input_ctxs; 502 | input.ctxs_count = 2; 503 | out_ctx.next = b; 504 | galgorithm_nway_merge(ctx, &input, &output); 505 | assert_sorted(ctx, b, n); 506 | } 507 | 508 | // Check n-way merge with n sorted lists each containing exactly one item. 509 | init_array(a, n); 510 | struct nway_merge_input_ctx *const nway_merge_input_ctxs = 511 | malloc(sizeof(nway_merge_input_ctxs[0]) * n); 512 | for (size_t i = 0; i < n; ++i) { 513 | struct nway_merge_input_ctx *const input_ctx = &nway_merge_input_ctxs[i]; 514 | input_ctx->next = a + i; 515 | input_ctx->end = a + (i + 1); 516 | } 517 | 518 | input.ctxs = nway_merge_input_ctxs; 519 | input.ctxs_count = n; 520 | out_ctx.next = b; 521 | galgorithm_nway_merge(ctx, &input, &output); 522 | assert_sorted(ctx, b, n); 523 | 524 | free(nway_merge_input_ctxs); 525 | 526 | free(b); 527 | 528 | printf("OK\n"); 529 | } 530 | 531 | static void item_deleter(void *item) 532 | { 533 | /* do nothing */ 534 | (void)item; 535 | } 536 | 537 | static void test_priority_queue(const struct gheap_ctx *const ctx, 538 | const size_t n, int *const a) 539 | { 540 | printf(" test_priority_queue(n=%zu) ", n); 541 | 542 | // Verify emptry priority queue. 543 | struct gpriority_queue *const q_empty = gpriority_queue_create(ctx, 544 | &item_deleter); 545 | assert(gpriority_queue_empty(q_empty)); 546 | assert(gpriority_queue_size(q_empty) == 0); 547 | 548 | // Verify non-empty priority queue. 549 | init_array(a, n); 550 | struct gpriority_queue *const q = gpriority_queue_create_from_array(ctx, 551 | &item_deleter, a, n); 552 | assert(!gpriority_queue_empty(q)); 553 | assert(gpriority_queue_size(q) == n); 554 | 555 | // Pop all items from the priority queue. 556 | int max_item = *(int *)gpriority_queue_top(q); 557 | for (size_t i = 1; i < n; ++i) { 558 | gpriority_queue_pop(q); 559 | assert(gpriority_queue_size(q) == n - i); 560 | assert(*(int *)gpriority_queue_top(q) <= max_item); 561 | max_item = *(int *)gpriority_queue_top(q); 562 | } 563 | assert(*(int *)gpriority_queue_top(q) <= max_item); 564 | gpriority_queue_pop(q); 565 | assert(gpriority_queue_empty(q)); 566 | 567 | // Push items to priority queue. 568 | for (size_t i = 0; i < n; ++i) { 569 | const int tmp = rand(); 570 | gpriority_queue_push(q, &tmp); 571 | assert(gpriority_queue_size(q) == i + 1); 572 | } 573 | 574 | // Interleave pushing and popping items in priority queue. 575 | max_item = *(int *)gpriority_queue_top(q); 576 | for (size_t i = 1; i < n; ++i) { 577 | gpriority_queue_pop(q); 578 | assert(*(int*)gpriority_queue_top(q) <= max_item); 579 | const int tmp = rand(); 580 | if (tmp > max_item) { 581 | max_item = tmp; 582 | } 583 | gpriority_queue_push(q, &tmp); 584 | } 585 | assert(gpriority_queue_size(q) == n); 586 | 587 | printf("OK\n"); 588 | } 589 | 590 | static void run_all(const struct gheap_ctx *const ctx, 591 | void (*func)(const struct gheap_ctx *, size_t, int *)) 592 | { 593 | int *const a = malloc(1001 * sizeof(*a)); 594 | 595 | for (size_t i = 1; i < 12; ++i) { 596 | func(ctx, i, a); 597 | } 598 | func(ctx, 1001, a); 599 | 600 | free(a); 601 | } 602 | 603 | static void test_all(const size_t fanout, const size_t page_chunks) 604 | { 605 | printf(" test_all(fanout=%zu, page_chunks=%zu) start\n", 606 | fanout, page_chunks); 607 | 608 | const struct gheap_ctx ctx_v = { 609 | .fanout = fanout, 610 | .page_chunks = page_chunks, 611 | .item_size = sizeof(int), 612 | .less_comparer = &less_comparer, 613 | .less_comparer_ctx = (void *)0, 614 | .item_mover = &item_mover, 615 | }; 616 | const struct gheap_ctx *const ctx = &ctx_v; 617 | 618 | /* Verify parent-child calculations for indexes close to zero and 619 | * indexes close to SIZE_MAX. 620 | */ 621 | static const size_t n = 1000000; 622 | test_parent_child(ctx, 1, n); 623 | test_parent_child(ctx, SIZE_MAX - n, n); 624 | 625 | run_all(ctx, test_is_heap); 626 | run_all(ctx, test_make_heap); 627 | run_all(ctx, test_sort_heap); 628 | run_all(ctx, test_push_heap); 629 | run_all(ctx, test_pop_heap); 630 | run_all(ctx, test_swap_max_item); 631 | run_all(ctx, test_restore_heap_after_item_increase); 632 | run_all(ctx, test_restore_heap_after_item_decrease); 633 | run_all(ctx, test_remove_from_heap); 634 | run_all(ctx, test_heapsort); 635 | run_all(ctx, test_partial_sort); 636 | run_all(ctx, test_nway_merge); 637 | run_all(ctx, test_nway_mergesort); 638 | run_all(ctx, test_priority_queue); 639 | 640 | printf(" test_all(fanout=%zu, page_chunks=%zu) OK\n", fanout, page_chunks); 641 | } 642 | 643 | static void main_test(void) 644 | { 645 | printf("main_test() start\n"); 646 | 647 | test_all(1, 1); 648 | test_all(2, 1); 649 | test_all(3, 1); 650 | test_all(4, 1); 651 | test_all(101, 1); 652 | 653 | test_all(1, 2); 654 | test_all(2, 2); 655 | test_all(3, 2); 656 | test_all(4, 2); 657 | test_all(101, 2); 658 | 659 | test_all(1, 3); 660 | test_all(2, 3); 661 | test_all(3, 3); 662 | test_all(4, 3); 663 | test_all(101, 3); 664 | 665 | test_all(1, 4); 666 | test_all(2, 4); 667 | test_all(3, 4); 668 | test_all(4, 4); 669 | test_all(101, 4); 670 | 671 | test_all(1, 101); 672 | test_all(2, 101); 673 | test_all(3, 101); 674 | test_all(4, 101); 675 | test_all(101, 101); 676 | 677 | printf("main_test() OK\n"); 678 | } 679 | 680 | int main(void) 681 | { 682 | srand(0); 683 | main_test(); 684 | return 0; 685 | } 686 | -------------------------------------------------------------------------------- /tests.cpp: -------------------------------------------------------------------------------- 1 | // Tests for C++03 and C++11 gheap, galgorithm and gpriority_queue. 2 | // 3 | // Pass -DGHEAP_CPP11 to compiler for gheap_cpp11.hpp tests, 4 | // otherwise gheap_cpp03.hpp will be tested. 5 | 6 | #include "galgorithm.hpp" 7 | #include "gheap.hpp" 8 | #include "gpriority_queue.hpp" 9 | 10 | #include // for min_element() 11 | #include 12 | #include // for srand(), rand() 13 | #include 14 | #include // for cout 15 | #include // for back_inserter 16 | #include 17 | #include // for pair 18 | 19 | #ifndef GHEAP_CPP11 20 | # include // for swap() 21 | #endif 22 | 23 | using namespace std; 24 | 25 | namespace { 26 | 27 | template 28 | void test_parent_child(const size_t start_index, const size_t n) 29 | { 30 | assert(start_index > 0); 31 | assert(start_index <= SIZE_MAX - n); 32 | 33 | cout << " test_parent_child(start_index=" << start_index << ", n=" << n << 34 | ") "; 35 | 36 | for (size_t i = 0; i < n; ++i) { 37 | const size_t u = start_index + i; 38 | size_t v = Heap::get_child_index(u); 39 | if (v < SIZE_MAX) { 40 | assert(v > u); 41 | v = Heap::get_parent_index(v); 42 | assert(v == u); 43 | } 44 | 45 | v = Heap::get_parent_index(u); 46 | assert(v < u); 47 | v = Heap::get_child_index(v); 48 | assert(v <= u && u - v < Heap::FANOUT); 49 | } 50 | 51 | cout << "OK" << endl; 52 | } 53 | 54 | template 55 | void test_is_heap(const size_t n) 56 | { 57 | assert(n > 0); 58 | 59 | cout << " test_is_heap(n=" << n << ") "; 60 | 61 | IntContainer a; 62 | 63 | // Verify that ascending sorted array creates one-item heap. 64 | a.clear(); 65 | for (size_t i = 0; i < n; ++i) { 66 | a.push_back(i); 67 | } 68 | assert(Heap::is_heap_until(a.begin(), a.end()) == a.begin() + 1); 69 | assert(Heap::is_heap(a.begin(), a.begin() + 1)); 70 | if (n > 1) { 71 | assert(!Heap::is_heap(a.begin(), a.end())); 72 | } 73 | 74 | // Verify that descending sorted array creates valid heap. 75 | a.clear(); 76 | for (size_t i = 0; i < n; ++i) { 77 | a.push_back(n - i); 78 | } 79 | assert(Heap::is_heap_until(a.begin(), a.end()) == a.end()); 80 | assert(Heap::is_heap(a.begin(), a.end())); 81 | 82 | // Verify that array containing identical items creates valid heap. 83 | a.clear(); 84 | for (size_t i = 0; i < n; ++i) { 85 | a.push_back(n); 86 | } 87 | assert(Heap::is_heap_until(a.begin(), a.end()) == a.end()); 88 | assert(Heap::is_heap(a.begin(), a.end())); 89 | 90 | cout << "OK" << endl; 91 | } 92 | 93 | template 94 | void init_array(IntContainer &a, const size_t n) 95 | { 96 | a.clear(); 97 | 98 | for (size_t i = 0; i < n; ++i) { 99 | a.push_back(rand()); 100 | } 101 | } 102 | 103 | template 104 | void assert_sorted_asc(const RandomAccessIterator &first, 105 | const RandomAccessIterator &last) 106 | { 107 | assert(last > first); 108 | 109 | const size_t size = last - first; 110 | for (size_t i = 1; i < size; ++i) { 111 | assert(first[i] >= first[i - 1]); 112 | } 113 | } 114 | 115 | template 116 | void assert_sorted_desc(const RandomAccessIterator &first, 117 | const RandomAccessIterator &last) 118 | { 119 | assert(last > first); 120 | 121 | const size_t size = last - first; 122 | for (size_t i = 1; i < size; ++i) { 123 | assert(first[i] <= first[i - 1]); 124 | } 125 | } 126 | 127 | bool less_comparer_desc(const int &a, const int &b) 128 | { 129 | return (b < a); 130 | } 131 | 132 | template 133 | void test_make_heap(const size_t n) 134 | { 135 | cout << " test_make_heap(n=" << n << ") "; 136 | 137 | IntContainer a; 138 | init_array(a, n); 139 | Heap::make_heap(a.begin(), a.end()); 140 | assert(Heap::is_heap(a.begin(), a.end())); 141 | 142 | cout << "OK" << endl; 143 | } 144 | 145 | template 146 | void test_sort_heap(const size_t n) 147 | { 148 | cout << " test_sort_heap(n=" << n << ") "; 149 | 150 | IntContainer a; 151 | 152 | // Test ascending sorting 153 | init_array(a, n); 154 | Heap::make_heap(a.begin(), a.end()); 155 | Heap::sort_heap(a.begin(), a.end()); 156 | assert_sorted_asc(a.begin(), a.end()); 157 | 158 | // Test descending sorting 159 | init_array(a, n); 160 | Heap::make_heap(a.begin(), a.end(), less_comparer_desc); 161 | Heap::sort_heap(a.begin(), a.end(), less_comparer_desc); 162 | assert_sorted_desc(a.begin(), a.end()); 163 | 164 | cout << "OK" << endl; 165 | } 166 | 167 | template 168 | void test_push_heap(const size_t n) 169 | { 170 | cout << " test_push_heap(n=" << n << ") "; 171 | 172 | IntContainer a; 173 | init_array(a, n); 174 | 175 | for (size_t i = 0; i < n; ++i) { 176 | Heap::push_heap(a.begin(), a.begin() + i + 1); 177 | } 178 | assert(Heap::is_heap(a.begin(), a.end())); 179 | 180 | cout << "OK" << endl; 181 | } 182 | 183 | template 184 | void test_pop_heap(const size_t n) 185 | { 186 | cout << " test_pop_heap(n=" << n << ") "; 187 | 188 | IntContainer a; 189 | init_array(a, n); 190 | 191 | Heap::make_heap(a.begin(), a.end()); 192 | for (size_t i = 0; i < n; ++i) { 193 | const int item = a[0]; 194 | Heap::pop_heap(a.begin(), a.end() - i); 195 | assert(item == *(a.end() - i - 1)); 196 | } 197 | assert_sorted_asc(a.begin(), a.end()); 198 | 199 | cout << "OK" << endl; 200 | } 201 | 202 | template 203 | void test_swap_max_item(const size_t n) 204 | { 205 | typedef typename IntContainer::iterator iterator; 206 | 207 | cout << " test_swap_max_item(n=" << n << ") "; 208 | 209 | IntContainer a; 210 | init_array(a, n); 211 | 212 | const size_t m = n / 2; 213 | 214 | if (m > 0) { 215 | Heap::make_heap(a.begin(), a.begin() + m); 216 | for (size_t i = m; i < n; ++i) { 217 | const int max_item = a[0]; 218 | Heap::swap_max_item(a.begin(), a.begin() + m, a[i]); 219 | assert(max_item == a[i]); 220 | assert(Heap::is_heap(a.begin(), a.begin() + m)); 221 | } 222 | } 223 | 224 | cout << "OK" << endl; 225 | } 226 | 227 | template 228 | void test_restore_heap_after_item_increase(const size_t n) 229 | { 230 | cout << " test_restore_heap_after_item_increase(n=" << n << ") "; 231 | 232 | IntContainer a; 233 | init_array(a, n); 234 | 235 | Heap::make_heap(a.begin(), a.end()); 236 | for (size_t i = 0; i < n; ++i) { 237 | const size_t item_index = rand() % n; 238 | const int old_item = a[item_index]; 239 | 240 | // Don't allow integer overflow. 241 | size_t fade = SIZE_MAX; 242 | do { 243 | // Division by zero is impossible here. 244 | a[item_index] = old_item + rand() % fade; 245 | fade /= 2; 246 | } while (a[item_index] < old_item); 247 | Heap::restore_heap_after_item_increase(a.begin(), a.begin() + item_index); 248 | assert(Heap::is_heap(a.begin(), a.end())); 249 | } 250 | 251 | cout << "OK" << endl; 252 | } 253 | 254 | template 255 | void test_restore_heap_after_item_decrease(const size_t n) 256 | { 257 | cout << " test_restore_heap_after_item_decrease(n=" << n << ") "; 258 | 259 | IntContainer a; 260 | init_array(a, n); 261 | 262 | Heap::make_heap(a.begin(), a.end()); 263 | for (size_t i = 0; i < n; ++i) { 264 | const size_t item_index = rand() % n; 265 | const int old_item = a[item_index]; 266 | 267 | // Don't allow integer underflow. 268 | size_t fade = SIZE_MAX; 269 | do { 270 | // Division by zero is impossible here. 271 | a[item_index] = old_item - rand() % fade; 272 | fade /= 2; 273 | } while (a[item_index] > old_item); 274 | Heap::restore_heap_after_item_decrease(a.begin(), a.begin() + item_index, 275 | a.end()); 276 | assert(Heap::is_heap(a.begin(), a.end())); 277 | } 278 | 279 | cout << "OK" << endl; 280 | } 281 | 282 | template 283 | void test_remove_from_heap(const size_t n) 284 | { 285 | cout << " test_remove_from_heap(n=" << n << ") "; 286 | 287 | IntContainer a; 288 | init_array(a, n); 289 | 290 | Heap::make_heap(a.begin(), a.end()); 291 | for (size_t i = 0; i < n; ++i) { 292 | const size_t item_index = rand() % (n - i); 293 | const int item = a[item_index]; 294 | Heap::remove_from_heap(a.begin(), a.begin() + item_index, a.end() - i); 295 | assert(Heap::is_heap(a.begin(), a.end() - i - 1)); 296 | assert(item == *(a.end() - i - 1)); 297 | } 298 | 299 | cout << "OK" << endl; 300 | } 301 | 302 | template 303 | void test_heapsort(const size_t n) 304 | { 305 | typedef galgorithm algorithm; 306 | 307 | cout << " test_heapsort(n=" << n << ") "; 308 | 309 | IntContainer a; 310 | 311 | // Verify ascending sorting with default less_comparer. 312 | init_array(a, n); 313 | algorithm::heapsort(a.begin(), a.end()); 314 | assert_sorted_asc(a.begin(), a.end()); 315 | 316 | // Verify descending sorting with custom less_comparer. 317 | init_array(a, n); 318 | algorithm::heapsort(a.begin(), a.end(), less_comparer_desc); 319 | assert_sorted_desc(a.begin(), a.end()); 320 | 321 | cout << "OK" << endl; 322 | } 323 | 324 | template 325 | void test_partial_sort(const size_t n) 326 | { 327 | typedef galgorithm algorithm; 328 | typedef typename IntContainer::iterator iterator; 329 | 330 | cout << " test_partial_sort(n=" << n << ") "; 331 | 332 | IntContainer a; 333 | 334 | // Check 0-items partial sort. 335 | init_array(a, n); 336 | algorithm::partial_sort(a.begin(), a.begin(), a.end()); 337 | 338 | // Check 1-item partial sort. 339 | if (n > 0) { 340 | init_array(a, n); 341 | algorithm::partial_sort(a.begin(), a.begin() + 1, a.end()); 342 | assert(min_element(a.begin(), a.end()) == a.begin()); 343 | } 344 | 345 | // Check 2-items partial sort. 346 | if (n > 1) { 347 | init_array(a, n); 348 | algorithm::partial_sort(a.begin(), a.begin() + 2, a.end()); 349 | assert_sorted_asc(a.begin(), a.begin() + 2); 350 | assert(min_element(a.begin() + 1, a.end()) == a.begin() + 1); 351 | } 352 | 353 | // Check n-items partial sort. 354 | init_array(a, n); 355 | algorithm::partial_sort(a.begin(), a.end(), a.end()); 356 | assert_sorted_asc(a.begin(), a.end()); 357 | 358 | // Check (n-1)-items partial sort. 359 | if (n > 0) { 360 | init_array(a, n); 361 | algorithm::partial_sort(a.begin(), a.end() - 1, a.end()); 362 | assert_sorted_asc(a.begin(), a.end()); 363 | } 364 | 365 | // Check (n-2)-items partial sort. 366 | if (n > 2) { 367 | init_array(a, n); 368 | algorithm::partial_sort(a.begin(), a.end() - 2, a.end()); 369 | assert_sorted_asc(a.begin(), a.end() - 2); 370 | assert(min_element(a.end() - 3, a.end()) == a.end() - 3); 371 | } 372 | 373 | cout << "OK" << endl; 374 | } 375 | 376 | template 377 | void test_nway_merge(const size_t n) 378 | { 379 | typedef galgorithm algorithm; 380 | typedef typename IntContainer::iterator iterator; 381 | 382 | cout << " test_nway_merge(n=" << n << ") "; 383 | 384 | IntContainer a, b; 385 | vector > input_ranges; 386 | 387 | // Check 1-way merge. 388 | init_array(a, n); 389 | b.clear(); 390 | input_ranges.clear(); 391 | algorithm::heapsort(a.begin(), a.end()); 392 | input_ranges.push_back(pair(a.begin(), a.end())); 393 | algorithm::nway_merge(input_ranges.begin(), input_ranges.end(), 394 | back_inserter(b)); 395 | assert_sorted_asc(b.begin(), b.end()); 396 | 397 | // Check 2-way merge. 398 | if (n > 1) { 399 | init_array(a, n); 400 | b.clear(); 401 | input_ranges.clear(); 402 | const iterator middle = a.begin() + n / 2; 403 | algorithm::heapsort(a.begin(), middle); 404 | algorithm::heapsort(middle, a.end()); 405 | input_ranges.push_back(pair(a.begin(), middle)); 406 | input_ranges.push_back(pair(middle, a.end())); 407 | algorithm::nway_merge(input_ranges.begin(), input_ranges.end(), 408 | back_inserter(b)); 409 | assert_sorted_asc(b.begin(), b.end()); 410 | } 411 | 412 | // Check n-way merge with n sorted lists each containing exactly one item. 413 | init_array(a, n); 414 | b.clear(); 415 | input_ranges.clear(); 416 | for (size_t i = 0; i < n; ++i) { 417 | input_ranges.push_back(pair(a.begin() + i, 418 | a.begin() + (i + 1))); 419 | } 420 | algorithm::nway_merge(input_ranges.begin(), input_ranges.end(), 421 | back_inserter(b)); 422 | assert_sorted_asc(b.begin(), b.end()); 423 | 424 | 425 | cout << "OK" << endl; 426 | } 427 | 428 | template 429 | void small_range_sorter(T *const first, T *const last, 430 | bool (&less_comparer)(const T &, const T &)) 431 | { 432 | galgorithm >::heapsort(first, last, less_comparer); 433 | } 434 | 435 | template 436 | void test_nway_mergesort(const size_t n) 437 | { 438 | typedef galgorithm algorithm; 439 | typedef typename IntContainer::value_type value_type; 440 | 441 | cout << " test_nway_mergesort(n=" << n << ") "; 442 | 443 | IntContainer a; 444 | 445 | // Verify n-way mergesort with default settings. 446 | init_array(a, n); 447 | algorithm::nway_mergesort(a.begin(), a.end()); 448 | assert_sorted_asc(a.begin(), a.end()); 449 | 450 | // Verify n-way mergesort with custom less_comparer. 451 | init_array(a, n); 452 | algorithm::nway_mergesort(a.begin(), a.end(), less_comparer_desc); 453 | assert_sorted_desc(a.begin(), a.end()); 454 | 455 | // Verify n-way mergesort with custom small_range_sorter. 456 | init_array(a, n); 457 | algorithm::nway_mergesort(a.begin(), a.end(), less_comparer_desc, 458 | small_range_sorter); 459 | assert_sorted_desc(a.begin(), a.end()); 460 | 461 | // Verify n-way mergesort with custom small_range_size. 462 | init_array(a, n); 463 | algorithm::nway_mergesort(a.begin(), a.end(), less_comparer_desc, 464 | small_range_sorter, 1); 465 | assert_sorted_desc(a.begin(), a.end()); 466 | 467 | // Verify n-way mergesort with custom subranges_count. 468 | init_array(a, n); 469 | algorithm::nway_mergesort(a.begin(), a.end(), less_comparer_desc, 470 | small_range_sorter, 2, 3); 471 | assert_sorted_desc(a.begin(), a.end()); 472 | 473 | cout << "OK" << endl; 474 | } 475 | 476 | template 477 | void test_priority_queue(const size_t n) 478 | { 479 | typedef typename IntContainer::value_type value_type; 480 | typedef gpriority_queue priority_queue; 481 | 482 | cout << " test_priority_queue(n=" << n << ") "; 483 | 484 | // Verify default constructor. 485 | priority_queue q_empty; 486 | assert(q_empty.empty()); 487 | assert(q_empty.size() == 0); 488 | 489 | // Verify non-empty priority queue. 490 | IntContainer a; 491 | init_array(a, n); 492 | priority_queue q(a.begin(), a.end()); 493 | assert(!q.empty()); 494 | assert(q.size() == n); 495 | 496 | // Verify swap(). 497 | q.swap(q_empty); 498 | assert(q.empty()); 499 | assert(!q_empty.empty()); 500 | assert(q_empty.size() == n); 501 | swap(q, q_empty); 502 | assert(!q.empty()); 503 | assert(q.size() == n); 504 | assert(q_empty.empty()); 505 | 506 | // Pop all items from the priority queue. 507 | int max_item = q.top(); 508 | for (size_t i = 1; i < n; ++i) { 509 | q.pop(); 510 | assert(q.size() == n - i); 511 | assert(q.top() <= max_item); 512 | max_item = q.top(); 513 | } 514 | assert(q.top() <= max_item); 515 | q.pop(); 516 | assert(q.empty()); 517 | 518 | // Push items to priority queue. 519 | for (size_t i = 0; i < n; ++i) { 520 | q.push(rand()); 521 | assert(q.size() == i + 1); 522 | } 523 | 524 | // Interleave pushing and popping items in priority queue. 525 | max_item = q.top(); 526 | for (size_t i = 1; i < n; ++i) { 527 | q.pop(); 528 | assert(q.top() <= max_item); 529 | const int tmp = rand(); 530 | if (tmp > max_item) { 531 | max_item = tmp; 532 | } 533 | q.push(tmp); 534 | } 535 | assert(q.size() == n); 536 | 537 | cout << "OK" << endl; 538 | } 539 | 540 | template 541 | void test_func(const Func &func) 542 | { 543 | for (size_t i = 1; i < 12; ++i) { 544 | func(i); 545 | } 546 | func(1001); 547 | } 548 | 549 | template 550 | void test_all() 551 | { 552 | cout << " test_all(Fanout=" << Fanout << ", PageChunks=" << PageChunks << 553 | ") start" << endl; 554 | 555 | typedef gheap heap; 556 | 557 | // Verify parent-child calculations for indexes close to zero and 558 | // indexes close to SIZE_MAX. 559 | static const size_t n = 1000000; 560 | test_parent_child(1, n); 561 | test_parent_child(SIZE_MAX - n, n); 562 | 563 | test_func(test_is_heap); 564 | test_func(test_make_heap); 565 | test_func(test_sort_heap); 566 | test_func(test_push_heap); 567 | test_func(test_pop_heap); 568 | test_func(test_swap_max_item); 569 | test_func(test_restore_heap_after_item_increase); 570 | test_func(test_restore_heap_after_item_decrease); 571 | test_func(test_remove_from_heap); 572 | test_func(test_heapsort); 573 | test_func(test_partial_sort); 574 | test_func(test_nway_merge); 575 | test_func(test_nway_mergesort); 576 | test_func(test_priority_queue); 577 | 578 | cout << " test_all(Fanout=" << Fanout << ", PageChunks=" << PageChunks << 579 | ") OK" << endl; 580 | } 581 | 582 | template 583 | void main_test(const char *const container_name) 584 | { 585 | cout << "main_test(" << container_name << ") start" << endl; 586 | 587 | test_all<1, 1, IntContainer>(); 588 | test_all<2, 1, IntContainer>(); 589 | test_all<3, 1, IntContainer>(); 590 | test_all<4, 1, IntContainer>(); 591 | test_all<101, 1, IntContainer>(); 592 | 593 | test_all<1, 2, IntContainer>(); 594 | test_all<2, 2, IntContainer>(); 595 | test_all<3, 2, IntContainer>(); 596 | test_all<4, 2, IntContainer>(); 597 | test_all<101, 2, IntContainer>(); 598 | 599 | test_all<1, 3, IntContainer>(); 600 | test_all<2, 3, IntContainer>(); 601 | test_all<3, 3, IntContainer>(); 602 | test_all<4, 3, IntContainer>(); 603 | test_all<101, 3, IntContainer>(); 604 | 605 | test_all<1, 4, IntContainer>(); 606 | test_all<2, 4, IntContainer>(); 607 | test_all<3, 4, IntContainer>(); 608 | test_all<4, 4, IntContainer>(); 609 | test_all<101, 4, IntContainer>(); 610 | 611 | test_all<1, 101, IntContainer>(); 612 | test_all<2, 101, IntContainer>(); 613 | test_all<3, 101, IntContainer>(); 614 | test_all<4, 101, IntContainer>(); 615 | test_all<101, 101, IntContainer>(); 616 | 617 | cout << "main_test(" << container_name << ") OK" << endl; 618 | } 619 | 620 | } // End of anonymous namespace. 621 | 622 | int main() 623 | { 624 | srand(0); 625 | main_test >("vector"); 626 | main_test >("deque"); 627 | } 628 | --------------------------------------------------------------------------------