2 * Generic reusable binary-heap maintenance code.
10 int coverage
; /* bit flags for various if clauses */
12 #define COVERAGE(x) ( coverage |= (1 << (x)) )
14 #define COVERAGE(x) ( (void)0 )
21 bheap_cmpfn_t compare
;
27 * Our array is zero-based. Therefore the children of 0 are 1 and
28 * 2; the children of 1 are 3 and 4; of 2 are 5 and 6, and so on.
29 * In other words, the children of node n are 2n+1 and 2n+2, and
30 * the parent of node n is floor((n-1)/2).
32 #define PARENT(n) ( ((n)-1)/2 )
33 #define LCHILD(n) ( 2*(n)+1 )
34 #define RCHILD(n) ( 2*(n)+2 )
37 * This macro calls the compare function, and returns TRUE if the
38 * two elements are the wrong way up and should be swapped.
40 * It doesn't _have_ to be called on a parent and child, but its
41 * return value assumes that. So you could call it on any two
42 * elements x,y and it would return TRUE if y ought to be the
45 #define MISORDERED(bh,parent,child) \
47 (bh)->compare((bh)->compare_ctx, \
48 (bh)->elts + (parent) * (bh)->eltsize, \
49 (bh)->elts + (child) * (bh)->eltsize) > 0 )
52 * This macro swaps two elements of the heap.
54 #define SWAP(bh,x,y) do { \
55 memcpy((bh)->elts + (bh)->maxelts * (bh)->eltsize, \
56 (bh)->elts + (x) * (bh)->eltsize, (bh)->eltsize); \
57 memcpy((bh)->elts + (x) * (bh)->eltsize, \
58 (bh)->elts + (y) * (bh)->eltsize, (bh)->eltsize); \
59 memcpy((bh)->elts + (y) * (bh)->eltsize, \
60 (bh)->elts + (bh)->maxelts * (bh)->eltsize, (bh)->eltsize); \
63 bheap
*bheap_new(int maxelts
, int eltsize
, int direction
,
64 bheap_cmpfn_t compare
, void *compare_ctx
)
66 bheap
*bh
= malloc(sizeof(bheap
));
71 * Allocate one extra element of space, to use for swapping
74 bh
->elts
= malloc(maxelts
* (eltsize
+1));
79 bh
->maxelts
= maxelts
;
80 bh
->eltsize
= eltsize
;
81 bh
->direction
= direction
;
82 bh
->compare
= compare
;
83 bh
->compare_ctx
= compare_ctx
;
88 void *bheap_add(bheap
*bh
, void *elt
)
92 if (bh
->nelts
>= bh
->maxelts
) {
99 * Add the element to the end of the array.
101 memcpy(bh
->elts
+ bh
->nelts
* bh
->eltsize
, elt
, bh
->eltsize
);
105 * Now swap it with its parent repeatedly to preserve the heap
118 if (MISORDERED(bh
, p
, i
)) {
124 break; /* we're done */
131 void *bheap_topmost(bheap
*bh
, void *elt
)
133 if (bh
->nelts
<= 0) {
139 memcpy(elt
, bh
->elts
, bh
->eltsize
);
143 void *bheap_remove(bheap
*bh
, void *elt
)
145 if (bh
->nelts
<= 0) {
151 memcpy(elt
, bh
->elts
, bh
->eltsize
);
160 * Move the highest-index element of the heap into the top
163 SWAP(bh
, bh
->nelts
, 0);
166 * Now repeatedly move it down the heap by swapping it with
167 * the more suitable of its children.
178 if (lc
>= bh
->nelts
) {
180 break; /* we've hit bottom */
183 if (rc
>= bh
->nelts
) {
185 * Special case: there is only one child to check.
188 if (MISORDERED(bh
, i
, lc
)) {
194 /* _Now_ we've hit bottom. */
199 * The common case: there are two children and we
200 * must check them both.
202 if (MISORDERED(bh
, i
, lc
) || MISORDERED(bh
, i
, rc
)) {
205 * Pick the more appropriate child to swap with
206 * (i.e. the one which would want to be the
207 * parent if one were above the other - as one
210 if (MISORDERED(bh
, lc
, rc
)) {
220 /* This element is in the right place; we're done. */
233 int bheap_count(bheap
*bh
)
238 void bheap_free(bheap
*bh
)
251 /* we _really_ need assertions enabled, for this test */
257 #define CHECK_COVERAGE(c, statement) do { \
258 coverage &= ~ (1 << (c)); \
260 assert(coverage & (1 << (c))); \
261 checked_coverage |= (1 << (c)); \
270 int intcmp(void *vctx
, const void *av
, const void *bv
)
272 const int *a
= (const int *)av
;
273 const int *b
= (const int *)bv
;
274 int *ctx
= (int *)vctx
;
276 assert(ctx
== &intcmp_ctx
);
287 int *ret
= bheap_add(bh
, &x
);
297 assert(bheap_count(bh
) == n
);
302 int out1
, *ret1
, out2
, *ret2
;
304 ret1
= bheap_topmost(bh
, &out1
);
305 ret2
= bheap_remove(bh
, &out2
);
308 assert(x
< 0); /* test the tests! */
309 assert(ret1
== NULL
);
310 assert(ret2
== NULL
);
314 assert(x
>= 0); /* test the tests! */
315 assert(ret1
== &out1
);
316 assert(ret2
== &out2
);
317 assert(out1
== out2
);
320 /* Now find x in _our_ array and remove it. */
321 for (i
= 0; i
< n
; i
++) {
322 assert(array
[i
] >= x
);
327 array
[n
-1] = array
[i
];
333 assert(i
< n
); /* we expect to have found it */
337 assert(bheap_count(bh
) == n
);
342 coverage
= checked_coverage
= 0;
344 bh
= bheap_new(MAX
, sizeof(int), +1, intcmp
, &intcmp_ctx
);
347 * Various sets of adds and removes which test all the code
348 * paths marked with COVERAGE() statements.
350 CHECK_COVERAGE(2, add(4));
351 CHECK_COVERAGE(3, add(7));
352 CHECK_COVERAGE(4, add(2));
353 CHECK_COVERAGE(1, add(6));
356 CHECK_COVERAGE(5, add(8));
358 CHECK_COVERAGE(0, add(9)); /* check the full-heap case */
360 CHECK_COVERAGE(7, rem(1));
361 CHECK_COVERAGE(8, rem(2));
362 CHECK_COVERAGE(9, rem(3));
363 CHECK_COVERAGE(14, rem(4));
367 CHECK_COVERAGE(19, rem(8));
368 CHECK_COVERAGE(6, rem(-1)); /* and check the empty-heap case */
369 CHECK_COVERAGE(8, rem(-1)); /* and check the empty-heap case */
374 CHECK_COVERAGE(12, rem(1));
381 CHECK_COVERAGE(13, rem(1));
389 CHECK_COVERAGE(17, rem(1));
398 CHECK_COVERAGE(16, rem(1));
410 CHECK_COVERAGE(18, rem(1));
411 CHECK_COVERAGE(15, rem(2));
413 CHECK_COVERAGE(10, rem(4));
414 CHECK_COVERAGE(11, rem(5));
419 * See what happens with compare equality.
454 * Interleave some adds and removes, turning the heap into a
455 * real priority queue rather than a glorified sorter.
457 * We add the digits of pi in order, and keep the heap size
458 * capped at 5 by extracting one before adding the next.
460 * Python code that generates this test sequence:
464 for c in "314159265358979323846264338327950288":
470 print (" rem(%d); /"+"* %s *"+"/") % (d, repr(list))
473 print (" add(%d); /"+"* %s *"+"/") % (c, repr(list))
478 print (" rem(%d); /"+"* %s *"+"/") % (d, repr(list))
485 add(4); /* [1, 3, 4] */
486 add(1); /* [1, 1, 3, 4] */
487 add(5); /* [1, 1, 3, 4, 5] */
488 rem(1); /* [1, 3, 4, 5] */
489 add(9); /* [1, 3, 4, 5, 9] */
490 rem(1); /* [3, 4, 5, 9] */
491 add(2); /* [2, 3, 4, 5, 9] */
492 rem(2); /* [3, 4, 5, 9] */
493 add(6); /* [3, 4, 5, 6, 9] */
494 rem(3); /* [4, 5, 6, 9] */
495 add(5); /* [4, 5, 5, 6, 9] */
496 rem(4); /* [5, 5, 6, 9] */
497 add(3); /* [3, 5, 5, 6, 9] */
498 rem(3); /* [5, 5, 6, 9] */
499 add(5); /* [5, 5, 5, 6, 9] */
500 rem(5); /* [5, 5, 6, 9] */
501 add(8); /* [5, 5, 6, 8, 9] */
502 rem(5); /* [5, 6, 8, 9] */
503 add(9); /* [5, 6, 8, 9, 9] */
504 rem(5); /* [6, 8, 9, 9] */
505 add(7); /* [6, 7, 8, 9, 9] */
506 rem(6); /* [7, 8, 9, 9] */
507 add(9); /* [7, 8, 9, 9, 9] */
508 rem(7); /* [8, 9, 9, 9] */
509 add(3); /* [3, 8, 9, 9, 9] */
510 rem(3); /* [8, 9, 9, 9] */
511 add(2); /* [2, 8, 9, 9, 9] */
512 rem(2); /* [8, 9, 9, 9] */
513 add(3); /* [3, 8, 9, 9, 9] */
514 rem(3); /* [8, 9, 9, 9] */
515 add(8); /* [8, 8, 9, 9, 9] */
516 rem(8); /* [8, 9, 9, 9] */
517 add(4); /* [4, 8, 9, 9, 9] */
518 rem(4); /* [8, 9, 9, 9] */
519 add(6); /* [6, 8, 9, 9, 9] */
520 rem(6); /* [8, 9, 9, 9] */
521 add(2); /* [2, 8, 9, 9, 9] */
522 rem(2); /* [8, 9, 9, 9] */
523 add(6); /* [6, 8, 9, 9, 9] */
524 rem(6); /* [8, 9, 9, 9] */
525 add(4); /* [4, 8, 9, 9, 9] */
526 rem(4); /* [8, 9, 9, 9] */
527 add(3); /* [3, 8, 9, 9, 9] */
528 rem(3); /* [8, 9, 9, 9] */
529 add(3); /* [3, 8, 9, 9, 9] */
530 rem(3); /* [8, 9, 9, 9] */
531 add(8); /* [8, 8, 9, 9, 9] */
532 rem(8); /* [8, 9, 9, 9] */
533 add(3); /* [3, 8, 9, 9, 9] */
534 rem(3); /* [8, 9, 9, 9] */
535 add(2); /* [2, 8, 9, 9, 9] */
536 rem(2); /* [8, 9, 9, 9] */
537 add(7); /* [7, 8, 9, 9, 9] */
538 rem(7); /* [8, 9, 9, 9] */
539 add(9); /* [8, 9, 9, 9, 9] */
540 rem(8); /* [9, 9, 9, 9] */
541 add(5); /* [5, 9, 9, 9, 9] */
542 rem(5); /* [9, 9, 9, 9] */
543 add(0); /* [0, 9, 9, 9, 9] */
544 rem(0); /* [9, 9, 9, 9] */
545 add(2); /* [2, 9, 9, 9, 9] */
546 rem(2); /* [9, 9, 9, 9] */
547 add(8); /* [8, 9, 9, 9, 9] */
548 rem(8); /* [9, 9, 9, 9] */
549 add(8); /* [8, 9, 9, 9, 9] */
550 rem(8); /* [9, 9, 9, 9] */
551 rem(9); /* [9, 9, 9] */
562 for (i
= 0; i
< 20; i
++) {
563 if (!(coverage
& (1 << i
)))
564 printf("coverage is missing %d\n", i
);
565 else if (!(checked_coverage
& (1 << i
)))
566 printf("checked_coverage is missing %d\n", i
);
570 printf("finished testing, no assertions failed\n");