$(o)/ucw/asio.test: $(o)/ucw/asio-t
endif
+$(o)/ucw/heap-bench: $(o)/ucw/heap-bench.o $(LIBUCW)
+PROGS+=$(o)/ucw/heap-bench
+
# The version of autoconf.h that is a part of the public API needs to have
# the internal symbols filtered out, so we generate ucw/autoconf.h in the
# configure script and let the public config.h refer to <ucw/autoconf.h>
--- /dev/null
+#define HEAP_PREFIX(x) TEST_PREFIX(x)
+#if defined(TEST_WANT_SORT)
+# define HEAP_WANT_INSERT
+# define HEAP_WANT_DELETE_MIN
+#endif
+#if defined(TEST_WANT_DIJKSTRA)
+# define HEAP_WANT_INSERT
+# define HEAP_WANT_DELETE_MIN
+# define HEAP_WANT_DECREASE
+#endif
+#if defined(TEST_WANT_MIX)
+# define HEAP_WANT_FIND_MIN
+# define HEAP_WANT_INSERT
+# define HEAP_WANT_REMOVE
+# define HEAP_WANT_DECREASE
+# define HEAP_WANT_INCREASE
+#endif
+struct TEST_PREFIX(heap);
+struct TEST_PREFIX(node);
+static inline int TEST_PREFIX(less)(struct TEST_PREFIX(heap) *heap, struct TEST_PREFIX(node) *a, struct TEST_PREFIX(node) *b);
+#include <ucw/heap-gen.h>
+
+struct TEST_PREFIX(item) {
+ struct TEST_PREFIX(node) n;
+ intmax_t key;
+};
+
+static inline int TEST_PREFIX(less)(struct TEST_PREFIX(heap) *heap UNUSED, struct TEST_PREFIX(node) *a, struct TEST_PREFIX(node) *b)
+{
+ test.num_cmp++;
+ return ((struct TEST_PREFIX(item) *)a)->key < ((struct TEST_PREFIX(item) *)b)->key;
+}
+
+#define P(x) TEST_PREFIX(x)
+
+#if defined(TEST_WANT_SORT)
+static void
+P(sort)(intmax_t *ary, uint n)
+{
+ test_init(STRINGIFY_EXPANDED(P(sort)));
+ struct P(heap) heap;
+ struct P(item) *items = mp_alloc(test.pool, n * sizeof(*items));
+ for (uint i = 0; i < n; i++)
+ items[i].key = ary[i];
+ start_clock();
+ P(init)(&heap);
+ for (uint i = 0; i < n; i++)
+ P(insert)(&heap, &items[i].n);
+ for (uint i = 0; i < n; i++)
+ {
+ struct P(item) *min = (struct P(item) *)P(delete_min)(&heap);
+ if (!min)
+ ASSERT(0);
+ update_checksum(min->key);
+ }
+ if (P(delete_min)(&heap))
+ ASSERT(0);
+ stop_clock();
+ test_done();
+}
+#endif
+
+#if defined(TEST_WANT_DIJKSTRA)
+static void
+P(dijkstra)(struct graph *g, uint start)
+{
+ test_init(STRINGIFY_EXPANDED(P(dijkstra)));
+ struct P(heap) heap;
+ struct P(item) *items = mp_alloc(test.pool, g->n * sizeof(*items));
+ for (uint i = 0; i < g->n; i++)
+ items[i].key = INTMAX_MAX;
+ items[start].key = 0;
+ start_clock();
+ P(init)(&heap);
+ for (uint v = 0; v < g->n; v++)
+ P(insert)(&heap, &items[v].n);
+ uint num_reachable = 0;
+ uintmax_t num_decrease = 0;
+ uintmax_t max_dist = 0;
+ while (1)
+ {
+ struct P(item) *min = (struct P(item) *)P(delete_min)(&heap);
+ if (!min || min->key == INTMAX_MAX)
+ break;
+ num_reachable++;
+ max_dist = min->key;
+ update_checksum(min->key);
+ uint u = min - items;
+ for (int e = g->v_first_edge[u]; e >= 0; e = g->e_next_edge[e])
+ {
+ uint v = g->e_dest[e];
+ intmax_t d = min->key + g->e_weight[e];
+ if (d < items[v].key)
+ {
+ items[v].key = d;
+ P(decrease)(&heap, &items[v].n);
+ num_decrease++;
+ }
+ }
+ }
+ stop_clock();
+ add_note("decrease:%ju reachable:%u max-dist:%ju", num_decrease, num_reachable, max_dist);
+ test_done();
+}
+#endif
+
+#if defined(TEST_WANT_MIX)
+static void
+P(mix)(uint n, uint m, uint k)
+{
+ test_init(STRINGIFY_EXPANDED(P(mix)));
+ struct eltpool *item_pool = ep_new(sizeof(struct P(item)), 100);;
+ struct P(item) **item_ary;
+ GARY_INIT(item_ary, 0);
+ struct P(heap) heap;
+ uintmax_t num_insert = 0, num_remove = 0, num_increase = 0, num_decrease = 0;
+ start_clock();
+ P(init)(&heap);
+ while (m--)
+ {
+ if (!GARY_SIZE(item_ary))
+ goto force_insert;
+ uint ins_rem_prob = 20;
+ uint ins_prob;
+ if (GARY_SIZE(item_ary) < n)
+ ins_prob = ins_rem_prob / 4 * 3;
+ else
+ ins_prob = ins_rem_prob / 4;
+ int r = random_max(100);
+ if ((r -= ins_prob) < 0)
+ {
+force_insert:
+ num_insert++;
+ struct P(item) *item = ep_alloc(item_pool);
+ item->key = random_max(k);
+ *GARY_PUSH(item_ary) = item;
+ P(insert)(&heap, &item->n);
+ }
+ else
+ {
+ uint i = random_max(GARY_SIZE(item_ary));
+ struct P(item) *item = item_ary[i];
+ if ((r -= ins_rem_prob - ins_prob) < 0)
+ {
+ num_remove++;
+ item_ary[i] = item_ary[GARY_SIZE(item_ary) - 1];
+ GARY_POP(item_ary);
+ P(remove)(&heap, &item->n);
+ ep_free(item_pool, item);
+ }
+ else if ((r -= (100 - ins_rem_prob) / 2) < 0)
+ {
+ num_decrease++;
+ item->key -= (int)random_max(k / 10 + 1) + 1;
+ P(decrease)(&heap, &item->n);
+ }
+ else
+ {
+ num_increase++;
+ item->key += (int)random_max(k / 10 + 1) + 1;
+ P(increase)(&heap, &item->n);
+ }
+ }
+ struct P(item) *min = (struct P(item) *)P(find_min)(&heap);
+ update_checksum(min ? min->key : INTMAX_MIN);
+ }
+ stop_clock();
+ GARY_FREE(item_ary);
+ ep_delete(item_pool);
+ add_note("insert:%ju remove:%ju decrease:%ju increase:%ju", num_insert, num_remove, num_decrease, num_increase);
+ test_done();
+}
+#endif
+
+#undef TEST_PREFIX
+#undef TEST_WANT_SORT
+#undef TEST_WANT_DIJKSTRA
+#undef TEST_WANT_MIX
+#undef P
--- /dev/null
+/*
+ * Tests & benchmarks of various heap data structures
+ *
+ * (c) 2018 Pavel Charvat <pchar@ucw.cz>
+ */
+
+#include <ucw/lib.h>
+#include <ucw/eltpool.h>
+#include <ucw/gary.h>
+#include <ucw/hashfunc.h>
+#include <ucw/mempool.h>
+#include <ucw/opt.h>
+#include <ucw/simple-lists.h>
+#include <ucw/time.h>
+
+#include <unistd.h>
+#include <sys/types.h>
+
+static int o_seed;
+
+static int o_heap_pairing_noparent;
+static int o_heap_pairing;
+static int o_heap_rank_pairing_noparent;
+static int o_heap_rank_pairing_type1;
+static int o_heap_rank_pairing_type2;
+
+static int o_test_type = -1;
+enum test_type {
+ TT_RANDOM_SORT,
+ TT_RANDOM_DIJKSTRA,
+ TT_RANDOM_MIX,
+};
+
+static uint o_n, o_m, o_k;
+
+static struct test {
+ struct mempool *pool;
+ const char *name;
+ timestamp_t timer;
+ uint time;
+ uint checksum;
+ uintmax_t num_cmp;
+ clist notes;
+} test;
+
+static void test_init(const char *name)
+{
+ ASSERT(!test.pool);
+ bzero(&test, sizeof(test));
+ test.pool = mp_new(4096);
+ test.name = name;
+ clist_init(&test.notes);
+ srand(o_seed);
+}
+
+static inline void update_checksum(uintmax_t val)
+{
+ test.checksum = (test.checksum + val + 17) * 1125899906842597UL;
+}
+
+static void FORMAT_CHECK(printf,1,2) add_note(const char *fmt, ...)
+{
+ va_list va;
+ va_start(va, fmt);
+ simp_append(test.pool, &test.notes)->s = mp_vprintf(test.pool, fmt, va);
+ va_end(va);
+}
+
+static void start_clock(void)
+{
+ init_timer(&test.timer);
+}
+
+static void stop_clock(void)
+{
+ test.time += get_timer(&test.timer);
+}
+
+static void test_done(void)
+{
+ char *notes = mp_printf(test.pool, "sec:%0.3f csum:0x%x cmp:%ju", 0.001 * test.time, test.checksum, test.num_cmp);
+ CLIST_FOR_EACH(simp_node *, n, test.notes)
+ notes = mp_printf_append(test.pool, notes, " %s", n->s);
+ msg(L_INFO, "TEST[%s] -> %s", test.name, notes);
+ mp_delete(test.pool);
+ test.pool = NULL;
+}
+
+struct graph {
+ uint n, m;
+ int *v_first_edge;
+ int *e_next_edge;
+ uint *e_dest;
+ uint *e_weight;
+};
+
+static struct graph *generate_random_graph(uint n, uint m, uint w_min, uint w_max)
+{
+ struct graph *g = xmalloc_zero(sizeof(*g));
+ g->n = n;
+ g->m = m;
+ g->v_first_edge = xmalloc(n * sizeof(*g->v_first_edge));
+ g->e_next_edge = xmalloc(m * sizeof(*g->e_next_edge));
+ g->e_dest = xmalloc(m * sizeof(*g->e_dest));
+ g->e_weight = xmalloc(m * sizeof(*g->e_weight));
+ for (uint v = 0; v < n; v++)
+ g->v_first_edge[v] = -1;
+ for (uint e = 0; e < m; e++)
+ {
+ uint u = random_max(n);
+ uint v = random_max(n);
+ g->e_next_edge[e] = g->v_first_edge[u];
+ g->v_first_edge[u] = e;
+ g->e_dest[e] = v;
+ g->e_weight[e] = w_min + random_max(w_max - w_min + 1);
+ }
+ return g;
+}
+
+static void free_graph(struct graph *g)
+{
+ xfree(g->v_first_edge);
+ xfree(g->e_next_edge);
+ xfree(g->e_dest);
+ xfree(g->e_weight);
+ xfree(g);
+}
+
+#define TEST_PREFIX(x) pairing_noparent_##x
+#define HEAP_PAIRING
+#define TEST_WANT_SORT
+#include <ucw/heap-bench-gen.h>
+
+#define TEST_PREFIX(x) pairing_##x
+#define HEAP_PAIRING
+#define TEST_WANT_SORT
+#define TEST_WANT_DIJKSTRA
+#define TEST_WANT_MIX
+#include <ucw/heap-bench-gen.h>
+
+#define TEST_PREFIX(x) rank_pairing_noparent_##x
+#define HEAP_RANK_PAIRING
+#define HEAP_RANK_PAIRING_TYPE1
+#define TEST_WANT_SORT
+#include <ucw/heap-bench-gen.h>
+
+#define TEST_PREFIX(x) rank_pairing_type1_##x
+#define HEAP_RANK_PAIRING
+#define HEAP_RANK_PAIRING_TYPE1
+#define TEST_WANT_SORT
+#define TEST_WANT_DIJKSTRA
+#define TEST_WANT_MIX
+#include <ucw/heap-bench-gen.h>
+
+#define TEST_PREFIX(x) rank_pairing_type2_##x
+#define HEAP_RANK_PAIRING
+#define HEAP_RANK_PAIRING_TYPE2
+#define TEST_WANT_SORT
+#define TEST_WANT_DIJKSTRA
+#define TEST_WANT_MIX
+#include <ucw/heap-bench-gen.h>
+
+static void FORMAT_CHECK(printf,1,2) test_start(const char *fmt, ...)
+{
+ msg(L_INFO, " ");
+ va_list va;
+ va_start(va, fmt);
+ vmsg(L_INFO, fmt, va);
+ va_end(va);
+ srand(o_seed + 12345);
+}
+
+static void test_random_sort(uint n, uint k)
+{
+ test_start("Testing randomized sort, n=%u k=%u", n, k);
+
+ uintmax_t *ary = xmalloc(n * sizeof(*ary));
+ for (uint i = 0; i < n; i++)
+ ary[i] = random_max(n);
+
+ if (o_heap_pairing_noparent)
+ pairing_noparent_sort(ary, n);
+ if (o_heap_pairing)
+ pairing_sort(ary, n);
+ if (o_heap_rank_pairing_noparent)
+ rank_pairing_noparent_sort(ary, n);
+ if (o_heap_rank_pairing_type1)
+ rank_pairing_type1_sort(ary, n);
+ if (o_heap_rank_pairing_type2)
+ rank_pairing_type2_sort(ary, n);
+
+ xfree(ary);
+}
+
+static void test_random_dijkstra(uint n, uint m, uint k)
+{
+ test_start("Testing randomized dijkstra, n=%u m=%u k=%u", n, m, k);
+
+ ASSERT(k);
+ struct graph *g = generate_random_graph(n, m, 1, k);
+ uint start = random_max(g->n);
+
+ if (o_heap_pairing)
+ pairing_dijkstra(g, start);
+ if (o_heap_rank_pairing_type1)
+ rank_pairing_type1_dijkstra(g, start);
+ if (o_heap_rank_pairing_type2)
+ rank_pairing_type2_dijkstra(g, start);
+
+ free_graph(g);
+}
+
+static void test_random_mix(uint n, uint m, uint k)
+{
+ test_start("Testing randomized mix, n=%u m=%u k=%u", n, m, k);
+
+ if (o_heap_pairing)
+ pairing_mix(n, m, k);
+ if (o_heap_rank_pairing_type1)
+ rank_pairing_type1_mix(n, m, k);
+ if (o_heap_rank_pairing_type2)
+ rank_pairing_type2_mix(n, m, k);
+}
+
+static struct opt_section options = {
+ OPT_ITEMS {
+ OPT_HELP("Usage: heap-bench [<options>] [<heap-type>] [<test-type>]"),
+ OPT_HELP(""),
+ OPT_HELP("Options:"),
+ OPT_HELP_OPTION,
+ OPT_INT('r', "random-seed", o_seed, OPT_REQUIRED_VALUE, "<val>\tUse this random seed (default: generate one)"),
+ OPT_HELP(""),
+ OPT_HELP("Heap types (default: compare all of them):"),
+ OPT_BOOL(0, "pairing-noparent", o_heap_pairing_noparent, 0, "\tPairing heaps with no parent pointers (without Decrease, etc.)"),
+ OPT_BOOL(0, "pairing", o_heap_pairing, 0, "\tPairing heaps"),
+ OPT_BOOL(0, "rank-pairing-noparent", o_heap_rank_pairing_noparent, 0, "\tRank pairing heaps with no parent pointers (without Decrease, etc.)"),
+ OPT_BOOL(0, "rank-pairing-type1", o_heap_rank_pairing_type1, 0, "\tType 1 of rank pairing heaps"),
+ OPT_BOOL(0, "rank-pairing-type2", o_heap_rank_pairing_type2, 0, "\tType 2 of rank pairing heaps"),
+ OPT_HELP(""),
+ OPT_HELP("Test type (default: a mix of various tests):"),
+ OPT_SWITCH(0, "random-sort", o_test_type, TT_RANDOM_SORT, 0, "\tSort a random sequence (options: -n<count>, -k<value-range>)"),
+ OPT_SWITCH(0, "random-dijkstra", o_test_type, TT_RANDOM_DIJKSTRA, 0, "\tFind all nearest paths from a vertex in random graph (options: -n<vertices>, -e<edges>, -k<edge-value-range>)"),
+ OPT_SWITCH(0, "random-mix", o_test_type, TT_RANDOM_MIX, 0, "\tRandomized mix of Insert, Remove, Decrease, Increase (options: -n<avg-nodes>, -e<operations>, -k<initial-value-range>)"),
+ OPT_UINT('n', NULL, o_n, OPT_REQUIRED_VALUE, NULL),
+ OPT_UINT('m', NULL, o_m, OPT_REQUIRED_VALUE, NULL),
+ OPT_UINT('k', NULL, o_k, OPT_REQUIRED_VALUE, NULL),
+ OPT_END
+ }
+};
+
+int main(int argc UNUSED, char **argv)
+{
+ o_seed = (u16)(hash_u32(getpid()) ^ get_timestamp());
+ opt_parse(&options, argv + 1);
+
+ msg(L_DEBUG, "Using random seed %u", o_seed);
+
+ if (1
+ && !o_heap_pairing_noparent
+ && !o_heap_pairing
+ && !o_heap_rank_pairing_noparent
+ && !o_heap_rank_pairing_type1
+ && !o_heap_rank_pairing_type2
+ )
+ {
+ o_heap_pairing_noparent = 1;
+ o_heap_pairing = 1;
+ o_heap_rank_pairing_noparent = 1;
+ o_heap_rank_pairing_type1 = 1;
+ o_heap_rank_pairing_type2 = 1;
+ }
+
+ switch (o_test_type)
+ {
+ case TT_RANDOM_SORT:
+ test_random_sort(o_n ? : 1000000, o_k ? : 1000000000);
+ break;
+
+ case TT_RANDOM_DIJKSTRA:
+ test_random_dijkstra(o_n ? : 300000, o_m ? : 3000000, o_k ? : 1000);
+ break;
+
+ case TT_RANDOM_MIX:
+ test_random_mix(o_n ? : 300000, o_m ? : 5000000, o_k ? : 10000);
+ break;
+
+ default:
+ test_random_sort(1000000, 1000000000);
+ test_random_sort(1000000, 1000);
+ test_random_dijkstra(300000, 3000000, 1000);
+ test_random_mix(300000, 5000000, 10000);
+ break;
+ }
+
+ return 0;
+}
--- /dev/null
+/*
+ * UCW Library -- Heap Data Structures: Code generator
+ *
+ * (c) 2018 Pavel Charvat <pchar@ucw.cz>
+ *
+ * This software may be freely distributed and used according to the terms
+ * of the GNU Lesser General Public License.
+ *
+ *
+ * References:
+ *
+ * [1] Michael L. Fredman, Robert Sedgewick, Daniel D. Sleator, Robert E. Tarjan,
+ * The pairing heap: A new form of self-adjusting heap,
+ * Algorithmica, 1(1–4):111–129, 1986.
+ *
+ * [2] Bernhard Haeupler, Siddhartha Sen, Robert E. Tarjan,
+ * Rank-Pairing Heaps,
+ * SIAM J. Computing, 40(6):1463–1485, 2011.
+ *
+ * [3] Daniel H. Larkin, Siddhartha Sen, Robert E. Tarjan,
+ * A Back-to-Basics Empirical Study of Priority Queues,
+ * 2014.
+ */
+
+#define P(x) HEAP_PREFIX(x)
+
+#if defined(HEAP_PAIRING)
+# if defined(HEAP_WANT_REPLACE)
+# define HEAP_WANT_REMOVE
+# define HEAP_WANT_INSERT
+# endif
+# if defined(HEAP_WANT_REMOVE) || defined(HEAP_WANT_DECREASE)
+# define HEAP_NODE_STORE_PARENT
+# endif
+
+#elif defined(HEAP_RANK_PAIRING)
+# if !defined(HEAP_RANK_PAIRING_TYPE1) && !defined(HEAP_RANK_PAIRING_TYPE2)
+# define HEAP_RANK_PAIRING_TYPE1
+# endif
+# if defined(HEAP_WANT_INCREASE) || defined(HEAP_WANT_REPLACE)
+# define HEAP_WANT_REMOVE
+# define HEAP_WANT_INSERT
+# endif
+# define HEAP_NODE_STORE_RANK
+# if defined(HEAP_WANT_REMOVE) || defined(HEAP_WANT_DECREASE)
+# define HEAP_NODE_STORE_PARENT
+# endif
+
+#else
+# error Missing heap type
+#endif
+
+typedef struct P(heap) *P(heap_p);
+typedef struct P(node) *P(node_p);
+
+struct P(heap) {
+ P(node_p) root;
+};
+
+struct P(node) {
+ P(node_p) left, right;
+#if defined(HEAP_NODE_STORE_PARENT)
+ P(node_p) parent;
+#endif
+#if defined(HEAP_NODE_STORE_RANK)
+ byte rank;
+#endif
+};
+
+static inline void
+P(init)(P(heap_p) h)
+{
+ h->root = NULL;
+}
+
+// @a and @b must be non-NULL roots of two half-trees, function merges them and returns new half-tree.
+// @parent and @right pointers in @a and @b can be undefined, but in resulting root they are not automatically cleared.
+static inline P(node_p)
+P(link)(P(heap_p) h, P(node_p) a, P(node_p) b)
+{
+ P(node_p) c;
+ if (P(less)(h, b, a))
+ {
+ c = a;
+ a = b;
+ b = c;
+ }
+ c = a->left;
+ a->left = b;
+ b->right = c;
+#if defined(HEAP_NODE_STORE_PARENT)
+ b->parent = a;
+ if (c)
+ c->parent = b;
+#endif
+#if defined(HEAP_NODE_STORE_RANK)
+ // "a->rank++" would not work for rank pairing heaps
+ a->rank = b->rank + 1;
+#endif
+ return a;
+}
+
+#if defined(HEAP_PAIRING) && (defined(HEAP_WANT_DELETE_MIN) || defined(HEAP_WANT_REMOVE) || defined(HEAP_WANT_INCREASE))
+// Merges list of half-trees (from decomposed right spine of a half-tree) into one half-tree,
+// using the two-pass pairing algorithm. Empty list is also supported.
+static P(node_p)
+P(two_pass)(P(heap_p) h, P(node_p) a)
+{
+ if (!a)
+ return NULL;
+
+ // First pass: Merge pairs of trees from left to right and create a link list of them
+ P(node_p) l = NULL, b, c;
+ do
+ {
+ b = a->right;
+ if (!b)
+ c = NULL;
+ else
+ {
+ c = b->right;
+ a = P(link)(h, a, b);
+ }
+ a->right = l;
+ l = a;
+ }
+ while (a = c);
+
+ // Second pass: Merge all trees in the link list from right to left
+ a = l;
+ l = l->right;
+ while (l)
+ {
+ b = l;
+ l = l->right;
+ a = P(link)(h, a, b);
+ }
+
+ // Fix undefined pointers in the new root
+ a->right = NULL;
+#if defined(HEAP_NODE_STORE_PARENT)
+ a->parent = NULL;
+#endif
+ return a;
+}
+#endif
+
+#if defined(HEAP_NODE_STORE_PARENT)
+// Removes @x from a right spine. @x must not be root. Does not update @parent and @right pointers of @x.
+static inline void
+P(cut_from_spine)(P(node_p) a)
+{
+ if (a->parent->left == a)
+ a->parent->left = a->right;
+ else
+ a->parent->right = a->right;
+ if (a->right)
+ a->right->parent = a->parent;
+}
+#endif
+
+#if defined(HEAP_RANK_PAIRING) && (defined(HEAP_WANT_DELETE_MIN) || defined(HEAP_WANT_REMOVE))
+static void
+P(cut_root_and_consolidate)(P(heap_p) h, P(node_p) a)
+{
+ P(node_p) min = NULL;
+ P(node_p) list, *list_tail = &list;
+ P(node_p) bucket[32];
+ byte bucket_ary[32];
+ byte bucket_count = 0;
+ uint bucket_mask = 0;
+
+ // We are going to execute very similar code for two lists in a short loop:
+ // 1) The right spine of root->left
+ // 2) Remaining heap's half-trees except the deleted root
+ P(node_p) x = a->left, stop = NULL;
+ while (1)
+ {
+ while (x != stop)
+ {
+ P(node_p) next = x->right;
+ if (!(bucket_mask & (1U << x->rank)))
+ {
+ bucket_mask |= 1U << x->rank;
+ bucket_ary[bucket_count++] = x->rank;
+ bucket[x->rank] = x;
+ }
+ else if (!bucket[x->rank])
+ {
+ bucket[x->rank] = x;
+ }
+ else
+ {
+ P(node_p) y = bucket[x->rank];
+ bucket[x->rank] = NULL;
+ x = P(link)(h, y, x);
+ *list_tail = x;
+ list_tail = &x->right;
+#if defined(HEAP_NODE_STORE_PARENT)
+ x->parent = NULL;
+#endif
+ if (!min || P(less)(h, x, min))
+ min = x;
+ }
+ x = next;
+ }
+ if (stop)
+ break;
+ stop = a;
+ x = a->right;
+ }
+
+ // Process remaining unpaired half-trees in buckets
+ for (byte i = 0; i < bucket_count; i++)
+ {
+ P(node_p) x = bucket[bucket_ary[i]];
+ if (!x)
+ continue;
+ *list_tail = x;
+ list_tail = &x->right;
+#if defined(HEAP_NODE_STORE_PARENT)
+ x->parent = NULL;
+#endif
+ if (!min || P(less)(h, x, min))
+ min = x;
+ }
+
+ // Close the resulting list of half-trees and update minimum
+ *list_tail = list; // list can be undefined here for an empty list, but it's OK
+ h->root = min;
+}
+#endif
+
+#if defined(HEAP_RANK_PAIRING) && (defined(HEAP_WANT_DECREASE) || defined(HEAP_WANT_REMOVE))
+static void
+P(cut_from_spine_and_update_ranks)(P(node_p) a)
+{
+ P(node_p) x = a->parent, y = a->right;
+ if (x->left == a)
+ x->left = y;
+ else
+ x->right = y;
+ s8 rank = -1;
+ if (y)
+ {
+ y->parent = x;
+ rank = y->rank;
+ }
+ while (1)
+ {
+ P(node_p) z = x->parent;
+ if (!z)
+ {
+ x->rank = rank + 1;
+ break;
+ }
+#if defined(HEAP_RANK_PAIRING_TYPE1)
+ if (!x->left || !x->right)
+ rank++;
+ else if (x->left->rank == x->right->rank)
+ rank = x->left->rank + 1;
+ else if (x->left->rank > x->right->rank)
+ rank = x->left->rank;
+ else
+ rank = x->right->rank;
+#else
+ if (!x->left || !x->right)
+ rank = rank > 0 ? rank : rank + 1;
+ else if (x->left->rank >= x->right->rank)
+ rank = x->left->rank + (x->left->rank - x->right->rank <= 1);
+ else
+ rank = x->right->rank + (x->right->rank - x->left->rank <= 1);
+#endif
+ if (x->rank <= (byte)rank)
+ break;
+ x->rank = (byte)rank;
+ y = x;
+ x = z;
+ }
+}
+#endif
+
+#if defined(HEAP_WANT_EMPTY)
+static inline bool
+P(empty)(P(heap_p) h)
+{
+ return h->root != NULL;
+}
+#endif
+
+#if defined(HEAP_WANT_FIND_MIN)
+static inline P(node_p)
+P(find_min)(P(heap_p) h)
+{
+ return h->root;
+}
+#endif
+
+#if defined(HEAP_WANT_INSERT)
+static void
+P(insert)(P(heap_p) h, P(node_p) a)
+{
+#if defined(HEAP_PAIRING)
+ a->left = a->right = NULL;
+#if defined(HEAP_NODE_STORE_PARENT)
+ a->parent = NULL;
+#endif
+ h->root = h->root ? P(link)(h, h->root, a) : a;
+
+#elif defined(HEAP_RANK_PAIRING)
+ a->left = NULL;
+#if defined(HEAP_NODE_STORE_PARENT)
+ a->parent = NULL;
+#endif
+#if defined(HEAP_NODE_STORE_RANK)
+ a->rank = 0;
+#endif
+ P(node_p) b = h->root;
+ if (!b)
+ {
+ a->right = a;
+ h->root = a;
+ }
+ else
+ {
+ a->right = b->right;
+ b->right = a;
+ if (P(less)(h, a, b))
+ h->root = a;
+ }
+#else
+# error HEAP_WANT_INSERT is not supported for this heap type
+#endif
+}
+#endif
+
+#if defined(HEAP_WANT_MERGE)
+static void
+P(merge)(P(heap_p) h, P(heap_p) g)
+{
+ P(node_p) a = g->root;
+ if (!g)
+ return;
+ g->root = NULL;
+#if defined(HEAP_PAIRING)
+ h->root = h->root ? P(link)(h, h->root, a) : a;
+#elif defined(HEAP_RANK_PAIRING)
+ P(node_p) b = h->root;
+ if (!b)
+ h->root = a;
+ else
+ {
+ P(node_p) c = a->right;
+ a->right = b->right;
+ b->right = c;
+ if (P(less)(h, a, b))
+ h->root = a;
+ }
+#else
+# error HEAP_WANT_MERGE is not supported for this heap type
+#endif
+}
+#endif
+
+#if defined(HEAP_WANT_DELETE_MIN)
+static P(node_p)
+P(delete_min)(P(heap_p) h)
+{
+ P(node_p) a = h->root;
+ if (!a)
+ return NULL;
+#if defined(HEAP_PAIRING)
+ h->root = P(two_pass)(h, a->left);
+#elif defined(HEAP_RANK_PAIRING)
+ P(cut_root_and_consolidate)(h, a);
+#else
+# error HEAP_WANT_DELETE_MIN is not supported for this heap type
+#endif
+ return a;
+}
+#endif
+
+#if defined(HEAP_WANT_REMOVE)
+static void
+P(remove)(P(heap_p) h, P(node_p) a)
+{
+#if defined(HEAP_PAIRING)
+ P(node_p) b = P(two_pass)(h, a->left);
+ if (!a->parent)
+ {
+ h->root = b;
+ return;
+ }
+ P(cut_from_spine)(a);
+ if (b)
+ h->root = P(link)(h, h->root, b);
+#elif defined(HEAP_RANK_PAIRING)
+ // Move @x to a root by emulating decrease; we don't need to update heap->root yet
+ if (a->parent)
+ {
+ P(cut_from_spine_and_update_ranks)(a);
+ a->parent = NULL;
+ a->right = h->root->right;
+ h->root->right = a;
+ }
+ P(cut_root_and_consolidate)(h, a);
+#else
+# error HEAP_WANT_REMOVE is not supported for this heap type
+#endif
+}
+#endif
+
+#if defined(HEAP_WANT_DECREASE)
+static void
+P(decrease)(P(heap_p) h, P(node_p) a)
+{
+#if defined(HEAP_PAIRING)
+ if (!a->parent)
+ return;
+ P(cut_from_spine)(a);
+ a->parent = a->right = NULL;
+ h->root = P(link)(h, h->root, a);
+#elif defined(HEAP_RANK_PAIRING)
+ if (!a->parent)
+ {
+ if (a == h->root)
+ return;
+ }
+ else
+ {
+ P(cut_from_spine_and_update_ranks)(a);
+ a->rank = a->left ? a->left->rank + 1 : 0;
+ a->parent = NULL;
+ a->right = h->root->right;
+ h->root->right = a;
+ }
+ if (P(less)(h, a, h->root))
+ h->root = a;
+#else
+# error HEAP_WANT_DECREASE is not supported for this heap type
+#endif
+}
+#endif
+
+#if defined(HEAP_WANT_INCREASE)
+static void
+P(increase)(P(heap_p) h, P(node_p) a)
+{
+#if defined(HEAP_PAIRING)
+ if (!a->left)
+ return;
+ P(node_p) b = P(two_pass)(h, a->left);
+ a->left = NULL;
+ h->root = P(link)(h, h->root, b);
+#elif defined(HEAP_RANK_PAIRING)
+ // FIXME: Optimize?
+ P(remove)(h, a);
+ P(insert)(h, a);
+#else
+# error HEAP_WANT_INCREASE is not supported for this heap type
+#endif
+}
+#endif
+
+#if defined(HEAP_WANT_REPLACE)
+static void
+P(replace)(P(heap_p) h, P(node_p) a)
+{
+#if defined(HEAP_PAIRING) || defined(HEAP_RANK_PAIRING)
+ P(remove)(h, a);
+ P(insert)(h, a);
+#else
+# error HEAP_WANT_REPLACE is not supported for this heap type
+#endif
+}
+#endif
+
+#undef HEAP_PREFIX
+
+#undef HEAP_PAIRING
+#undef HEAP_RANK_PAIRING
+#undef HEAP_RANK_PAIRING_TYPE1
+#undef HEAP_RANK_PAIRING_TYPE2
+
+#undef HEAP_WANT_EMPTY
+#undef HEAP_WANT_FIND_MIN
+#undef HEAP_WANT_INSERT
+#undef HEAP_WANT_MERGE
+#undef HEAP_WANT_DELETE_MIN
+#undef HEAP_WANT_REMOVE
+#undef HEAP_WANT_DECREASE
+#undef HEAP_WANT_INCREASE
+#undef HEAP_WANT_REPLACE
+
+#undef P
+#undef HEAP_NODE_STORE_PARENT
+#undef HEAP_NODE_STORE_RANK