1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335
|
/* Heaps and priority queues.
* See TH Cormen, CE Leiserson, and RL Rivest, _Introduction to Algorithms_, MIT Press, 1999.
*
* Contents:
* 1. The <ESL_HEAP> object: creation, access.
* 2. Rest of the API: inserting, extracting values.
* 3. Debugging, development.
* 4. Internal functions.
* 5. Unit tests.
* 6. Test driver.
*/
#include "esl_config.h"
#include <stdlib.h>
#include <stdio.h>
#include "easel.h"
#include "esl_heap.h"
static int heap_grow(ESL_HEAP *hp);
static void iheapify (ESL_HEAP *hp, int idx);
/*****************************************************************
* 1. The <ESL_HEAP> object.
*****************************************************************/
/* Function: esl_heap_ICreate()
* Synopsis: Create a heap for storing integers.
*
* Purpose: Create a heap for storing integers. <maxormin> is
* <eslHEAP_MIN> or <eslHEAP_MAX>; it states whether
* minimum or maximum values are sorted to the top of the
* heap.
*
* Args: maxormin : <eslHEAP_MIN | eslHEAP_MAX>
*
* Returns: a pointer to the new heap.
*
* Throws: <NULL> on allocation failure.
*/
ESL_HEAP *
esl_heap_ICreate(int maxormin)
{
ESL_HEAP *hp = NULL;
int status;
ESL_DASSERT1(( maxormin == eslHEAP_MIN || maxormin == eslHEAP_MAX));
ESL_ALLOC(hp, sizeof(ESL_HEAP));
hp->idata = NULL;
hp->n = 0;
hp->maxormin = maxormin;
ESL_ALLOC(hp->idata, sizeof(int) * eslHEAP_INITALLOC);
hp->nalloc = eslHEAP_INITALLOC;
return hp;
ERROR:
esl_heap_Destroy(hp);
return NULL;
}
/* Function: esl_heap_GetCount()
* Synopsis: Returns the number of items in the heap.
*/
int
esl_heap_GetCount(ESL_HEAP *hp)
{
return hp->n;
}
/* Function: esl_heap_IGetTopVal()
* Synopsis: Peeks at and returns the best (topmost) value in the heap.
*
* Purpose: Peek at the best (topmost) value in heap <hp> and
* return it. The heap is unaffected. If the heap is
* empty, return 0.
*/
int
esl_heap_IGetTopVal(ESL_HEAP *hp)
{
return (hp->n ? hp->idata[0] : 0);
}
/* Function: esl_heap_Reuse()
* Synopsis: Reuse a heap.
*
* Purpose: As an alternative to destroy'ing an old heap and
* create'ing a new one, empty this heap and reinitialize
* it, as if it is a freshly created heap of the same
* data type and same <maxormin>.
*
* Returns: <eslOK>
*/
int
esl_heap_Reuse(ESL_HEAP *hp)
{
hp->n = 0;
return eslOK;
}
/* Function: esl_heap_Destroy()
* Synopsis: Free a heap.
*
* Purpose: Destroys heap <hp>, of any data type.
*
* Returns: (void)
*
* Throws: (no abnormal error conditions)
*/
void
esl_heap_Destroy(ESL_HEAP *hp)
{
if (hp)
{
if (hp->idata) free(hp->idata);
free (hp);
}
}
/*****************************************************************
* 2. Rest of the API: inserting, extracting values
*****************************************************************/
/* Function: esl_heap_IInsert()
* Synopsis: Insert a value into the heap.
*
* Purpose: Insert value <val> into heap <hp>.
*
* Returns: <eslOK> on success.
*
* Throws: <eslEMEM> on allocation failure.
*/
int
esl_heap_IInsert(ESL_HEAP *hp, int val)
{
int idx, parentidx;
int status;
if (hp->n == hp->nalloc && (status = heap_grow(hp)) != eslOK) return status;
hp->n++;
idx = hp->n - 1;
while (idx > 0 && (hp->maxormin == eslHEAP_MIN ? hp->idata[ESL_HEAP_PARENT(idx)] > val : hp->idata[ESL_HEAP_PARENT(idx)] < val))
{
parentidx = ESL_HEAP_PARENT(idx);
hp->idata[idx] = hp->idata[parentidx];
idx = parentidx;
}
hp->idata[idx] = val;
return eslOK;
}
/* Function: esl_heap_IExtractTop()
* Synopsis: Extract the top value from the heap.
*
* Purpose: Extract the best (topmost) value from heap <hp>.
* Delete it from the heap. Return it in <*opt_val>.
*
* To simply delete the topmost value (without retrieving
* its value), pass <NULL> for <opt_val>.
* If the heap is empty, return <eslEOD>, and
* <*opt_val> is 0.
*
* Returns: <eslOK> on success, and <*opt_val> is the extracted
* topmost value.
*
* <eslEOD> if the heap is empty; <*opt_val> is 0.
*
* Throws: (no abnormal error conditions)
*/
int
esl_heap_IExtractTop(ESL_HEAP *hp, int *opt_val)
{
int bestval;
if (hp->n == 0) { *opt_val = 0; return eslEOD; }
bestval = hp->idata[0];
hp->idata[0] = hp->idata[hp->n-1];
hp->n--;
iheapify(hp, 0);
if (opt_val) *opt_val = bestval;
return eslOK;
}
/*****************************************************************
* 3. Debugging, development.
*****************************************************************/
int
esl_heap_Validate(ESL_HEAP *hp, char *errbuf)
{
int idx, lidx, ridx;
for (idx = 0; idx < hp->n; idx++)
{
lidx = ESL_HEAP_LEFT(idx);
ridx = lidx+1;
if (lidx < hp->n && ( hp->maxormin == eslHEAP_MIN ? hp->idata[lidx] < hp->idata[idx] : hp->idata[lidx] > hp->idata[idx] ))
ESL_FAIL(eslFAIL, errbuf, "at %d (value %d): left child %d (value %d) is better", idx, hp->idata[idx], lidx, hp->idata[lidx]);
if (ridx < hp->n && ( hp->maxormin == eslHEAP_MIN ? hp->idata[ridx] < hp->idata[idx] : hp->idata[ridx] > hp->idata[idx] ))
ESL_FAIL(eslFAIL, errbuf, "at %d (value %d): right child %d (value %d) is better", idx, hp->idata[idx], ridx, hp->idata[ridx]);
}
return eslOK;
}
/*****************************************************************
* 4. Internal functions
*****************************************************************/
static int
heap_grow(ESL_HEAP *hp)
{
int status;
if (hp->idata) {
ESL_REALLOC(hp->idata, sizeof(int) * (hp->nalloc*2));
hp->nalloc += hp->nalloc;
}
return eslOK;
ERROR:
return eslEMEM;
}
static void
iheapify(ESL_HEAP *hp, int idx)
{
int bestidx = idx;
int leftidx, rightidx;
while (1) /* while loop avoids recursive heapify call */
{
leftidx = ESL_HEAP_LEFT(idx);
rightidx = leftidx+1;
if (leftidx < hp->n && (hp->maxormin == eslHEAP_MIN ? hp->idata[leftidx] < hp->idata[idx] : hp->idata[leftidx] > hp->idata[idx]) ) bestidx = leftidx;
if (rightidx < hp->n && (hp->maxormin == eslHEAP_MIN ? hp->idata[rightidx] < hp->idata[bestidx] : hp->idata[rightidx] > hp->idata[bestidx]) ) bestidx = rightidx;
if (bestidx == idx) break; /* nothing needed to be changed: either because <idx> satisfies heap property, or because it has no children */
ESL_SWAP(hp->idata[idx], hp->idata[bestidx], int);
idx = bestidx;
}
}
/*****************************************************************
* 5. Unit tests
*****************************************************************/
#ifdef eslHEAP_TESTDRIVE
#include "esl_random.h"
static void
utest_sorting(ESL_RANDOMNESS *rng)
{
char *msg = "utest_sorting():: unit test failure";
ESL_HEAP *hp = NULL;
int *val = NULL;
int nv = 1 + esl_rnd_Roll(rng, 10000);
char errbuf[eslERRBUFSIZE];
int i,n2,x;
/* Create an array of numbers 1..nv in randomized order. */
if ( (val = malloc(sizeof(int) * nv)) == NULL) esl_fatal("utest_sorting():: allocation failed");
for (i = 0; i < nv; i++) val[i] = i+1;
for (n2 = nv; n2 > 1; n2--)
{ /* a compact Fisher-Yates shuffle. Can't put the Roll() into the ESL_SWAP(), because it's a macro: avoid double evaluation */
i = esl_rnd_Roll(rng, n2);
ESL_SWAP( val[i], val[n2-1], int);
}
/* Add those numbers (in their randomized order) to a min heap */
if ( (hp = esl_heap_ICreate(eslHEAP_MIN)) == NULL) esl_fatal(msg);
for (i = 0; i < nv; i++)
if (esl_heap_IInsert(hp, val[i]) != eslOK) esl_fatal(msg);
if (esl_heap_Validate(hp, errbuf) != eslOK) esl_fatal("utest: heap validation fails: %s", errbuf);
/* Now if we pull numbers off the heap, they'll come off in sorted order, 1..nv */
for (i = 1; i <= nv; i++)
{
if (esl_heap_IExtractTop(hp, &x) != eslOK) esl_fatal(msg);
if (x != i) esl_fatal(msg);
if (hp->n != nv-i) esl_fatal(msg);
}
esl_heap_Destroy(hp);
free(val);
}
#endif /*eslHEAP_TESTDRIVE*/
/*****************************************************************
* 6. Test driver
*****************************************************************/
#ifdef eslHEAP_TESTDRIVE
#include "easel.h"
#include "esl_getopts.h"
static ESL_OPTIONS options[] = {
/* name type default env range togs reqs incomp help docgrp */
{"-h", eslARG_NONE, FALSE, NULL, NULL, NULL, NULL, NULL, "show help and usage", 0},
{"-s", eslARG_INT, "0", NULL, NULL, NULL, NULL, NULL, "set random number seed to <n>", 0},
{ 0,0,0,0,0,0,0,0,0,0},
};
static char usage[] = "[-options]";
static char banner[] = "test driver for ESL_HEAP: heaps and priority queues";
int
main(int argc, char **argv)
{
ESL_GETOPTS *go = esl_getopts_CreateDefaultApp(options, 0, argc, argv, banner, usage);
ESL_RANDOMNESS *rng = esl_randomness_Create(esl_opt_GetInteger(go, "-s"));
fprintf(stderr, "## %s\n", argv[0]);
fprintf(stderr, "# rng seed = %" PRIu32 "\n", esl_randomness_GetSeed(rng));
utest_sorting(rng);
fprintf(stderr, "# status = ok\n");
esl_getopts_Destroy(go);
esl_randomness_Destroy(rng);
return 0;
}
#endif /*eslHEAP_TESTDRIVE*/
|