Distances on Directed Graphs in R
at main 1481 lines 45 kB view raw
1/*** Trinomial Heap Implementation ***/ 2/* 3 * Mark Padgham, adapted from code by Shane Saunders 4 */ 5 6/* This version is implemented using the node-pair pointer structure; that is, 7 * nodes have a partner pointer, so that nodes can be paired. In this 8 * implementation, a nodes child pointer points to its highest dimension child 9 * node. 10 */ 11 12#include "triheap_ext.h" 13#include <cmath> 14//#if SHOW_trih_ext 15#include <cstdio> 16#include <stdexcept> 17//#endif 18 19 20#define TRUE 1 21#define FALSE 0 22 23/*--- TriHeapExt (public methods) -------------------------------------------*/ 24 25/* --- Constructor --- 26 * creates and returns a pointer to a trinomial heap. Argument 27 * maxNodes specifies the maximum number of nodes the heap can contain. 28 */ 29TriHeapExt::TriHeapExt(size_t n) 30{ 31#if SHOW_trih_ext 32 Rcpp::Rcout << "init, "; 33 Rcpp::Rcout.flush (); 34#endif 35 36 /* The maximum number of nodes and the maximum number of trees allowed. */ 37 maxNodes = n; 38 maxTrees = 1 + static_cast <size_t> (log(static_cast <double> (n)) / 39 log (3.0)); 40 41 /* The tolerance trigger of the heap. That is, t+1; the number used for 42 * detecting when we are over the tolerance and need to cleanup active 43 * nodes. 44 */ 45 activeLimit = maxTrees; 46 47 /* Allocate space for an array of pointers to trees, and nodes in the heap. 48 * Initialise all array entries to zero, that is, NULL pointers. 49 */ 50 trees = new TriHeapExtNode *[maxTrees]; 51 for (size_t i = 0; i < maxTrees; i++) 52 trees[i] = std::nullptr_t (); 53 54 nodes = new TriHeapExtNode *[n]; 55 for (size_t i = 0; i < n; i++) 56 nodes[i] = std::nullptr_t (); 57 58 /* Allocate space for: 59 * - an unordered array of pointers to active nodes. 60 * - an array of pointers to queues of active nodes. 61 * - an array of pointers to entries in the candidates queue. 62 */ 63 activeNodes = new TriHeapExtNode *[activeLimit]; 64 for (size_t i = 0; i < activeLimit; i++) 65 activeNodes[i] = std::nullptr_t (); 66 67 activeQueues = new ActiveItem *[activeLimit-1]; 68 for (size_t i = 0; i < activeLimit-1; i++) 69 activeQueues[i] = std::nullptr_t (); 70 71 candidateItems = new CandidateItem *[activeLimit-1]; 72 for (size_t i = 0; i < activeLimit-1; i++) 73 candidateItems[i] = std::nullptr_t (); 74 75 /* We begin with no nodes in the heap. */ 76 itemCount = 0; 77 activeCount = 0; 78 candQueueHead = std::nullptr_t (); 79 80 /* The value of the heap helps to keep track of the maximum dimension while 81 * nodes are inserted or deleted. 82 */ 83 treeSum = 0; 84 85 /* For experimental purposes, we keep a count of the number of key 86 * comparisons. 87 */ 88 compCount = 0; 89} 90 91/* --- Destructor --- 92*/ 93TriHeapExt::~TriHeapExt() 94{ 95#if SHOW_trih_ext 96 Rcpp::Rcout << "free, "; 97 Rcpp::Rcout.flush (); 98#endif 99 100 for (size_t i = 0; i < maxNodes; i++) { 101 delete nodes[i]; 102 } 103 104 delete [] nodes; 105 delete [] trees; 106 delete [] activeNodes; 107 delete [] activeQueues; 108 delete [] candidateItems; 109} 110 111/* --- insert() --- 112 * inserts item $item$ with associated key $k$ into the heap. 113 */ 114void TriHeapExt::insert(size_t item, double k) 115{ 116 TriHeapExtNode *newNode; 117 118#if SHOW_trih_ext 119 Rcpp::Rcout << "insert, "; 120 Rcpp::Rcout.flush (); 121#endif 122 123 /* Create an initialise the new node. The parent pointer will be set to 124 * NULL by meld(). 125 */ 126 newNode = new TriHeapExtNode; 127 newNode->child = std::nullptr_t (); 128 newNode->extra = FALSE; 129 newNode->left = newNode->right = std::nullptr_t (); 130 newNode->partner = std::nullptr_t (); 131 132 newNode->activeEntry = std::nullptr_t (); 133 134 newNode->dim = 0; 135 newNode->item = item; 136 newNode->key = k; 137 138 /* Maintain a pointer to item's new node in the heap. */ 139 nodes[item] = newNode; 140 141 /* Meld the new node into the heap. */ 142 meld(newNode); 143 144 /* Update the heap's node count. */ 145 itemCount++; 146} 147 148/* --- deleteMin() --- 149 * Deletes and returns the minimum item from the heap. 150 */ 151size_t TriHeapExt::deleteMin() 152{ 153 static TriHeapExtNode meldListHeader; 154 155 TriHeapExtNode *minNode, *child, *next, *partner; 156 TriHeapExtNode *tail, *breakNode, *firstChild; 157 TriHeapExtNode *l, *parent, *childZero, *childHigher; 158 TriHeapExtNode *nextPartner, *nextParent, *nextFirstChild; 159 TriHeapExtNode *node, *nodePartner; 160 TriHeapExtNode *nextChildZero = std::nullptr_t (), 161 *nextChildHigher = std::nullptr_t (); 162 double k, k2; 163 size_t d, nextDim, v, item; 164 size_t wasExtra; 165 166#if SHOW_trih_ext 167 dump(); 168 Rcpp::Rcout << "deleteMin, "; 169 Rcpp::Rcout.flush (); 170#endif 171 172 /* First we determine the maximum dimension tree in the heap. */ 173 v = treeSum; 174 d = 0; 175 while(v) { 176 v = v >> 1; 177 d++; 178 }; 179 d--; 180 181 /* Now locate the root node with the smallest key, scanning from the 182 * maximum dimension root position, down to dimension 0 root position. 183 * At the same time, scan active nodes. Note that the maximum dimension of 184 * a active node is one less than the maximum dimension main trunk since we 185 * never get active nodes on a main trunk. 186 */ 187 minNode = trees[d]; 188 k = minNode->key; 189 while(d > 0) { 190 d--; 191 next = trees[d]; 192 if(next) { 193 compCount++; 194 if((k2 = next->key) < k) { 195 k = k2; 196 minNode = next; 197 } 198 } 199 200 } 201 size_t i = activeCount; 202 while(i > 0) { 203 i--; 204 next = activeNodes[i]; 205 compCount++; 206 if((k2 = next->key) < k) { 207 k = k2; 208 minNode = next; 209 } 210 } 211 212 213#if SHOW_trih_ext 214 Rcpp::Rcout << "on vertex no " << minNode->item; 215 Rcpp::Rcout.flush (); 216#endif 217 218 /* 219 * The node-pair tree containing the minimum node must be broken up into 220 * a linked list of node-pair sub-trees to be melded back into the heap. 221 * This linked list first includes any children trees broken of the minimum 222 * node. 223 * 224 */ 225 tail = &meldListHeader; 226 227 /* A nodes child pointer always points to its highest dimension child, 228 * so child->right is the smallest dimension. For melding the linked list 229 * starting at child->right, we terminate the circular link with a NULL 230 * pointer. 231 * 232 * For the linked list, only the right pointer is used, so the value of the 233 * left pointer will be left undefined. 234 */ 235 child = minNode->child; 236 if(child) { 237 tail->right = child->right; 238 node = tail = child; 239 240 /* Nodes in this break up may go from active to inactive. */ 241 do { 242 node = node->right; 243 nodePartner = node->partner; 244 245 if(node->activeEntry) { 246 deactivate(node); 247 if(nodePartner->activeEntry) deactivate(nodePartner); 248 } 249 /* Note that we do not need to check a second child for activeness 250 * if the first child was not active. 251 */ 252 253 } while(node != tail); 254 } 255 256 /* 257 * The linked list also includes higher trees that become broken up in 258 * cases where a non-root (i.e. active) minimum node was removed. 259 * 260 */ 261 262 /* During higher break-up breakNode points to the node that `appears' to 263 * have been removed. Initially this is minNode. 264 */ 265 d = minNode->dim; 266 breakNode = minNode; 267 partner = breakNode->partner; 268 firstChild = breakNode->extra ? partner : breakNode; 269 parent = firstChild->parent; /* parent pointer of the broken node. */ 270 271 /* If the minimum node was active then deactivate it, and propagate 272 * break-up through the tree until the main trunk is reached. 273 * Nodes on the main trunk are treated like first child and second child 274 * with no parent. 275 */ 276 if(parent) { 277 278 deactivate(minNode); 279 280 childZero = parent->child->right; 281 childHigher = firstChild->right; 282 283 /* If `partner' is active then deactivate it and determine the order of 284 * `partner' and `parent' in the trunk resulting from break-up. Otherwise 285 * parent is the first node on the trunk. 286 */ 287 if(partner->activeEntry) { 288 deactivate(partner); 289 compCount++; 290 if(partner->key < parent->key) { 291 tail->right = partner; 292 tail = partner; 293 } 294 else { 295 tail->right = parent; 296 tail = parent; 297 } 298 } 299 else { 300 tail->right = parent; 301 tail = parent; 302 } 303 304 while(1) { 305 /* At the start of the loop links are not yet updated to account for 306 * the node that appears to have been removed. 307 * parent - points to the parent of the last broken node. 308 * partner - points to the partner of the last broken node. 309 * firstChild - is the first child on the trunk of the broken node. 310 * The smaller of parent and partner is pointed to by the linked list, 311 * Since the linked list is updated in advance. 312 */ 313 314 315 /* Make the partner of breakNode `parent's partner. Remember the 316 * old value of the parents partner pointer for the next dimension. 317 * Also remember the value of parent->dim. The code has been written 318 * this way because certain pointer updates overwrite pointers that 319 * are needed later on. 320 * Note, if we must deactivate parent, this must be done before 321 * changing its dimension. 322 */ 323 if(parent->activeEntry) deactivate(parent); 324 nextDim = parent->dim; 325 parent->dim = d; 326 nextPartner = parent->partner; 327 parent->partner = partner; 328 partner->partner = parent; 329 330 /* For the last node pair added to the linked list 331 * (i.e. (parent,partner) or (partner,parent)), we ensure that the 332 * correct node is labelled as extra. 333 */ 334 wasExtra = parent->extra; 335 tail->extra = FALSE; 336 tail->partner->extra = TRUE; 337 338 /* Obtain future values of pointer variables now. This is done because 339 * constructing the linked list overwrites pointers that are followed. 340 */ 341 if(wasExtra) { 342 nextFirstChild = nextPartner; 343 } 344 else { 345 nextFirstChild = parent; 346 } 347 nextParent = nextFirstChild->parent; 348 if(nextParent) { 349 nextChildZero = nextParent->child->right; 350 nextChildHigher = nextFirstChild->right; 351 } 352 353 354 /* Add the child pairs of `parent' that have a greater dimension than 355 * firstChild to the list of broken trunks. Keep a pointer to 356 * parent->child->right for use below. 357 */ 358 if(parent->child != firstChild) { 359 node = tail; 360 tail->right = childHigher; 361 tail = parent->child; 362 363 /* Nodes in this break up may go from active to inactive. 364 */ 365 do { 366 node = node->right; 367 nodePartner = node->partner; 368 if(node->activeEntry) { 369 deactivate(node); 370 if(nodePartner->activeEntry) deactivate(nodePartner); 371 } 372 } while(node != tail); 373 } 374 375 376 /* Update the list of children of `parent' to only include those of 377 * lower dimension than firstChild. 378 */ 379 if(d) { 380 /* Lower dimension children exist. Note that tail currently points 381 * to the highest dimension child. 382 */ 383 l = firstChild->left; 384 l->right = childZero; 385 childZero->left = l; 386 parent->child = l; 387 } 388 else { 389 /* No lower dimension children. */ 390 parent->child = std::nullptr_t (); 391 } 392 393 394 /* Now continue break up at 1 dimension higher up by treating `parent' 395 * as the node that has been broken. 396 * Note that on ending, nextParent will be NULL, and we only require 397 * `partner' and d to be updated before exiting. 398 */ 399 partner = nextPartner; 400 d = nextDim; 401 if(!nextParent) break; 402 breakNode = parent; 403 parent = nextParent; 404 firstChild = nextFirstChild; 405 406 407 /* Pointers to the dimension zero child of parent, and the next highest 408 * dimension child from firstChild. 409 */ 410 childZero = nextChildZero; 411 childHigher = nextChildHigher; 412 413 414 /* Update the linked list in advance to point to the next breakNode, 415 * usually `parent', but possibly `partner' if `partner' is active. 416 */ 417 418 /* `partner' may be active, so we may need to swap the order of 419 * `partner' and `parent' in the trunk resulting from break-up. 420 */ 421 if(partner->activeEntry) { 422 deactivate(partner); 423 compCount++; 424 if(partner->key < parent->key) { 425 /* We make the linked list point to `partner' instead of 426 * `parent', and make parent an extra node. 427 */ 428 tail->right = partner; 429 tail = partner; 430 continue; /* Back to start of loop. */ 431 } 432 } 433 434 tail->right = parent; 435 tail = parent; 436 437 }} /* if-while */ 438 439 /* Terminate the list of trees to be melded. */ 440 tail->right = std::nullptr_t (); 441 442 /* Break up always propagates up to the main trunk level. After break up 443 * the length the main trunk decreases by one. The current tree position 444 * will become empty unless breakNode has a partner node. 445 */ 446 if(partner) { 447 partner->partner = std::nullptr_t (); 448 449 if(partner->extra) { 450 partner->extra = FALSE; 451 partner->parent = std::nullptr_t (); 452 partner->left = partner->right = partner; 453 trees[d] = partner; 454 } 455 } 456 else { 457 trees[d] = std::nullptr_t (); 458 treeSum -= (1 << d); 459 } 460 itemCount--; 461 462 /* Meld the linked list of trunks resulting from break-up into the main 463 * trunk level of the heap. 464 */ 465 if(meldListHeader.right) meld(meldListHeader.right); 466 467 468 /* Record the vertex no to return. */ 469 item = minNode->item; 470 471 /* Delete the old minimum node. */ 472 nodes[item] = std::nullptr_t (); 473 delete minNode; 474 475 return item; 476} 477 478/* --- decreaseKey() --- 479 * Decrease the key of item $item$ in the heap to the value $newValue$. It is 480 * up to the user to ensure that newValue is in-fact less than or equal to the 481 * current value. 482 */ 483void TriHeapExt::decreaseKey(size_t item, double newValue) 484{ 485 TriHeapExtNode *v, *v2, *w, *w2, *p, *above, *partner, *activeNode; 486 TriHeapExtNode *l, *r, *lowChild, *highChild, *node; 487 ActiveItem *activeEntry; 488 size_t d; 489 490#if SHOW_trih_ext 491 dump(); 492 Rcpp::Rcout << "decreaseKey on vn = " << item << " (" << newValue << ")"; 493 Rcpp::Rcout.flush (); 494#endif 495 496 497 /* Pointer v points to the decreased node. */ 498 v = nodes[item]; 499 v->key = newValue; 500 d = v->dim; /* dimension */ 501 502 v2 = v->partner; /* partner */ 503 if(v->extra) { 504 p = v2->parent; /* parent */ 505 above = v2; 506 } 507 else { 508 above = p = v->parent; /* parent */ 509 } 510 511 512 /* Determine if rearrangement is necessary */ 513 514 /* If v is a root node, then rearrangement is not necessary. */ 515 if(!above) return; 516 517 /* If v is already active then rearrangement is not necessary. */ 518 if(v->activeEntry) { 519 /* If v is a second child and its key has became less than its partner, 520 * which will also be active, then swap to maintain the correct 521 * ordering. 522 */ 523 if(v->extra && v->key < above->key) { 524 v->extra = FALSE; 525 v2->extra = TRUE; 526 replaceChild(v2, v); 527 } 528 return; 529 } 530 531 if(p) { 532 /* Non-main trunk. */ 533 534 if(v->extra) { 535 /* v is a second child. If necessary, we swap with the first 536 * child to maintain the correct ordering. 537 */ 538 539 compCount++; 540 if(v->key < above->key) { 541 /* swap */ 542 v->extra = FALSE; 543 v2->extra = TRUE; 544 replaceChild(v2, v); 545 } 546 else { 547 /* If v remains as a second child, then only activate it if 548 * the first child is active. 549 */ 550 if(v2->activeEntry) activate(v); 551 552 if(activeCount == activeLimit) goto rearrange; /* see below */ 553 554 return; 555 } 556 } 557 558 /* At this point, v is a first child. */ 559 560 activate(v); 561 562 /* If the number of active nodes is at tolerance level we must perform 563 * some rearrangement. 564 */ 565 if(activeCount == activeLimit) goto rearrange; /* see below */ 566 567 /* Otherwise it is okay to exit. */ 568 return; 569 570 } 571 572 /* Otherwise, v is the second node on a main trunk. */ 573 574 compCount++; 575 if(v->key < above->key) { 576 /* If v is smaller, we swap it with the first child (i.e. the root 577 * node) to maintain the correct ordering. 578 */ 579 v->extra = FALSE; 580 v2->extra = TRUE; 581 v->parent = std::nullptr_t (); 582 v->left = v->right = v; 583 trees[d] = v; 584 return; 585 } 586 587 /* Otherwise, no rearrangement is required since heap order is still 588 * satisfied. 589 */ 590 return; 591 592 /*------------------------------*/ 593rearrange: 594 595 /* Get a candidate for rearrangement. */ 596 d = candQueueHead->dim; 597 activeEntry = activeQueues[d]; 598 599 activeNode = activeEntry->node; 600 if(activeNode->extra) { 601 v = activeNode->partner; 602 v2 = activeNode; 603 } 604 else { 605 v = activeNode; 606 v2 = activeNode->partner; 607 } 608 p = v->parent; 609 610 /* If we have two active nodes on the same trunk. */ 611 if(v2->activeEntry) { 612 deactivate(v2); 613 614 /* If the 2nd child of these is less than the parent, then 615 * do promotion. 616 */ 617 if(v2->key < v->parent->key) goto promote; 618 619 return; 620 } 621 622 /* Try the second trunk. */ 623 activeNode = activeEntry->next->node; 624 if(activeNode->extra) { 625 w = activeNode->partner; 626 w2 = activeNode; 627 } 628 else { 629 w = activeNode; 630 w2 = activeNode->partner; 631 } 632 633 /* If we have two active nodes on the same trunk. */ 634 if(w2->activeEntry) { 635 deactivate(w2); 636 637 /* If the 2nd child of these is less than the parent, then 638 * do promotion. 639 */ 640 if(w2->key < w->parent->key) { 641 v = w; v2 = w2; 642 p = w->parent; 643 goto promote; 644 } 645 646 return; 647 } 648 649 /* The final rearrangement always pairs v with w and v2 with w2. */ 650 v->partner = w; w->partner = v; 651 v2->partner = w2; w2->partner = v2; 652 653 /* Determine the ordering in the rearrangement. */ 654 compCount++; 655 if(v2->key < w2->key) { 656 /* Make the (1st child, 2nd child) pair (v2,w2), replacing (v,v2). */ 657 v2->extra = FALSE; 658 replaceChild(v, v2); 659 660 compCount++; 661 if(v->key < w->key) { 662 /* Make the pair (v,w), replacing (w,w2). */ 663 w->extra = TRUE; 664 replaceChild(w,v); 665 deactivate(w); 666 667 compCount++; 668 if(w->key < v->parent->key) { 669 /* Both v and w are inconsistent, continue with promotion. 670 * Update v2, and p; 671 */ 672 v2 = w; 673 p = v->parent; 674 goto promote; /* see below */ 675 } 676 677 /* Otherwise, although w was active, it is not inconsistent. In 678 * this case we do not do promotion. Instead, only v remains an 679 * active node, although v may not be inconsistent. 680 * (Avoids a key comparison to check if v is consistent) 681 */ 682 return; 683 } 684 else { 685 /* Make the pair (w,v), replacing (w,w2). */ 686 v->extra = TRUE; 687 deactivate(v); 688 689 compCount++; 690 if(v->key < w->parent->key) { 691 /* Both v and w are inconsistent, so continue with promotion. 692 * Update v, v2, and p. 693 */ 694 v2 = v; 695 v = w; 696 p = w->parent; 697 goto promote; /* see below */ 698 } 699 700 /* Only w is possibly inconsistent, so it remains an active node. 701 */ 702 return; 703 } 704 } 705 else { 706 /* Make the pair (w2,v2), replacing (w,w2). */ 707 w2->extra = FALSE; 708 replaceChild(w, w2); 709 710 compCount++; 711 if(v->key < w->key) { 712 /* Make the pair (v,w), replacing (v,v2). */ 713 w->extra = TRUE; 714 deactivate(w); 715 716 compCount++; 717 if(w->key < v->parent->key) { 718 /* Both v and w are inconsistent, so continue with promotion. 719 * Update v2. 720 */ 721 v2 = w; 722 goto promote; /* see below */ 723 } 724 725 /* Only v is possibly inconsistent, so it remains an active node. 726 */ 727 return; 728 } 729 else { 730 /* Make the pair (w,v), replacing (v,v2). */ 731 v->extra = TRUE; 732 replaceChild(v,w); 733 deactivate(v); 734 735 compCount++; 736 if(v->key < w->parent->key) { 737 /* Both v and w are inconsistent, so continue with promotion. 738 * Update v, v2. 739 */ 740 v2 = v; 741 v = w; 742 goto promote; 743 } 744 745 /* Only w is possibly inconsistent, so it remains an active node. 746 */ 747 return; 748 } 749 } 750 751 /*------------------------------*/ 752promote: 753 /* Promotion Code. Reached if the loop has not exited. Uses variables 754 * p, v, v2, and d. Must ensure that on the trunk (p,v,v2), both v and v2 755 * are inconsistent nodes, so that (v,v2,p) will give heap ordering. 756 * Node v should still be active, and node v2 should have been deactivated. 757 * Node v will later become active at a higher dimension. 758 */ 759 deactivate(v); 760 761 /* First we make v2 a child node of v. */ 762 v2->extra = FALSE; 763 addChild(v, v2); 764 765 766 /* Then v replaces p. Any child nodes of p that have a higher dimension 767 * than v will become child nodes of v. Only child nodes of lower 768 * dimension than v will be left under p. 769 */ 770 771 v->dim = p->dim; 772 partner = v->partner = p->partner; 773 highChild = p->child; 774 775 if(d) { 776 /* v has lower dimension siblings. */ 777 l = p->child = v->left; 778 r = highChild->right; 779 l->right = r; 780 r->left = l; 781 } 782 else { 783 /* v was an only child. */ 784 p->child = std::nullptr_t (); 785 } 786 787 if(highChild != v) { 788 /* v has higher dimension siblings. Add them to the list of v's 789 * children, and update their parent pointer. Note that v currently 790 * has at least one child, since v2 was made a child of v. 791 */ 792 lowChild = v->right; 793 l = v->child; 794 r = v->child->right; 795 l->right = lowChild; 796 lowChild->left = l; 797 r->left = highChild; 798 highChild->right = r; 799 800 v->child = highChild; 801 802 node = v; 803 do { 804 node = node->right; 805 node->parent = v; 806 } while(node != highChild); 807 } 808 809 /* partner may be NULL if p is a root node, so don't update 810 * partner->partner yet. See update below. 811 */ 812 813 /* If p was an extra node no further pointer updates are required. */ 814 815 /* Further pointer updates are only needed if p is a first child or a root 816 * node. 817 */ 818 if(!p->extra) { 819 if(p->parent) { 820 /* p is non-root node and a first child. */ 821 partner->partner = v; 822 replaceChild(p, v); 823 } 824 else { 825 /* p is a root node, so update the tree pointer. */ 826 if(partner) partner->partner = v; 827 trees[p->dim] = v; 828 v->left = v->right = v; 829 v->parent = std::nullptr_t (); 830 } 831 832 /* p will become an extra node, see below. */ 833 p->extra = TRUE; 834 } 835 else { 836 /* If p was an extra node then v becomes an extra node. */ 837 partner->partner = v; 838 v->extra = TRUE; 839 } 840 841 /* Finally, make p the partner of node v2 (i.e. the 2nd child of node v). 842 * Note we cant update the dimension of p yet until we deactivate it. 843 */ 844 v2->partner = p; 845 p->partner = v2; 846 847 /* If v is a second child we may need to swap it with the first child to 848 * maintain correct ordering. If v remains as a second child, we do not 849 * make it active, unless the first child is also active. 850 */ 851 if(v->extra) { 852 v2 = v->partner; 853 854 compCount++; 855 if(v->key < v2->key) { 856 /* swap */ 857 v->extra = FALSE; 858 v2->extra = TRUE; 859 860 /* Note there is a special case, where a main trunk is reached. */ 861 if(v2->parent) { 862 /* Non-main trunk */ 863 replaceChild(v2, v); 864 } 865 else { 866 /* Main trunk: v replaces v2 as the root node. */ 867 v->extra = FALSE; 868 v2->extra = TRUE; 869 v->parent = std::nullptr_t (); 870 v->left = v->right = v; 871 trees[v->dim] = v; 872 873 /* Don't make v active. 874 * If necessary, deactivate p (which v replaced). 875 */ 876 if(p->activeEntry) deactivate(p); 877 p->dim = d; 878 return; 879 } 880 } 881 else if(!v2->activeEntry) { 882 /* Don't make v active. This is always the case for main trunks. 883 * If necessary, deactivate p (which v replaced). 884 */ 885 if(p->activeEntry) deactivate(p); 886 p->dim = d; 887 return; 888 } 889 } 890 else { 891 /* Don't swap, and if at main trunk level, don't make v active either. 892 */ 893 if(!v->parent) { 894 /* If necessary, deactivate p (which v replaced). */ 895 if(p->activeEntry) deactivate(p); 896 p->dim = d; 897 return; 898 } 899 } 900 901 /* The result of the above code never puts active nodes on a main trunk. 902 * If at main trunk level, the function will have exited above. 903 */ 904 905 /* If p was an active node, it no longer is, and v takes its place as an 906 * active node. 907 */ 908 if(p->activeEntry) { 909 replaceActive(p, v); 910 } 911 else { 912 /* Otherwise make v active. */ 913 activate(v); 914 } 915 p->dim = d; 916} 917 918/*--- TriHeapExt (private methods) ------------------------------------------*/ 919 920/* --- meld() --- 921 * Melds the linked list of trees pointed to by $treeList$ into the heap 922 * pointed to by $h$. This function uses the 'right' sibling pointer of nodes 923 * to traverse the linked list from lower dimension nodes to higher 924 * dimension nodes. It expects the last nodes `right' pointer to be NULL. 925 */ 926void TriHeapExt::meld(TriHeapExtNode *treeList) 927{ 928 TriHeapExtNode *next, *addTree; 929 TriHeapExtNode *carryTree; 930 size_t d; 931 932#if SHOW_trih_ext 933 Rcpp::Rcout << "meld - "; 934 Rcpp::Rcout.flush (); 935#endif 936 937 /* addTree points to the tree to be merged. */ 938 addTree = treeList; 939 940 carryTree = std::nullptr_t (); 941 942 do { 943 /* addTree() gets merged into the heap, and also carryTree if one 944 * exists from a previous merge. 945 */ 946 947 /* Keep a pointer to the next tree and remove sibling and parent links 948 * from the current tree. The dimension of the next tree is always 949 * one greater than the dimension of the previous tree, so this merging 950 * is like an addition of two ternary numbers. 951 * 952 * Note that if addTree is NULL and the loop has not exited, then 953 * there is only a carryTree to be merged, so treat it like addTree. 954 */ 955 next = std::nullptr_t (); 956 if(addTree) { 957 next = addTree->right; 958 addTree->right = addTree->left = addTree; 959 addTree->parent = std::nullptr_t (); 960 } 961 else { 962 addTree = carryTree; 963 carryTree = std::nullptr_t (); 964 } 965 966#if SHOW_trih_ext 967 Rcpp::Rcout << addTree->item << ", "; 968 Rcpp::Rcout.flush (); 969#endif 970 971 /* First we merge addTree with carryTree, if there is one. Note that 972 * carryTree contains only one node in its main trunk, and addTree 973 * has at most two, so the result is at most one 3-node trunk, which is 974 * treated as a 1-node main trunk one dimension higher up. 975 */ 976 if(carryTree) { 977 compCount += merge(&addTree, &carryTree); 978 } 979 980 /* After the merge, if addTree is NULL, then the resulting tree 981 * pointed to by carryTree carries to higher entry, so we do not need 982 * to merge anything into the existing main trunk. 983 * If addTree is not NULL we add it to the existing main trunk. 984 */ 985 if(addTree) { 986 d = addTree->dim; 987 if(trees[d]) { 988 /* Nodes already in this main trunk position, so merge. */ 989 compCount += merge(&trees[d], &addTree); 990 if(!trees[d]) treeSum -= (1 << d); 991 carryTree = addTree; 992 } 993 else { 994 /* No nodes in this main trunk position, so use addTree. */ 995 trees[d] = addTree; 996 treeSum += (1 << d); 997 } 998 } 999 1000 /* Obtain a pointer to the next tree to add. */ 1001 addTree = next; 1002 1003 /* We continue if there is still a node in the list to be merged, or 1004 * a carry tree remains to be merged. 1005 */ 1006 } while(addTree || carryTree); 1007} 1008 1009/* --- activate() --- 1010 * Add an inactive node to the queue of active nodes. It is up to the user of 1011 * this function to ensure that the node was not already active. 1012 */ 1013void TriHeapExt::activate(TriHeapExtNode *node) 1014{ 1015 size_t d; 1016 ActiveItem *activeEntry, *first, *last; 1017 CandidateItem *candidate, *lastC; 1018 1019 /* Note that we maintain doubly linked lists. This allows active items 1020 * and candidates to be removed in O(1) time. The circular linking allows 1021 * us to access the last element in the list in O(1) time. 1022 */ 1023 1024 /* Add n to the array of active nodes. */ 1025 size_t i = activeCount++; 1026 activeNodes[i] = node; 1027 1028 /* Create an entry for n in the list of pointers to active nodes. */ 1029 activeEntry = new ActiveItem; 1030 activeEntry->node = node; 1031 activeEntry->position = i; 1032 node->activeEntry = activeEntry; 1033 1034 /* Insertion depends on whether the list is empty or not. */ 1035 d = node->dim; 1036 1037 if((first = activeQueues[d])) { 1038 /* At least one item already. */ 1039 last = first->prev; 1040 last->next = activeEntry; 1041 activeEntry->prev = last; 1042 activeEntry->next = first; 1043 first->prev = activeEntry; 1044 1045 /* If there was originally one item, but now two, then insert a new 1046 * entry into the candidates queue. 1047 */ 1048 if(first == last) { 1049 candidate = new CandidateItem; 1050 candidate->dim = d; 1051 candidateItems[d] = candidate; 1052 1053 if(candQueueHead) { 1054 /* At least one candidate is already in the queue. */ 1055 lastC = candQueueHead->prev; 1056 lastC->next = candidate; 1057 candidate->prev = lastC; 1058 candidate->next = candQueueHead; 1059 candQueueHead->prev = candidate; 1060 } 1061 else { 1062 /* Inserting into empty queue. */ 1063 candQueueHead = candidate; 1064 candidate->next = candidate->prev = candidate; 1065 } 1066 } 1067 } 1068 else { 1069 /* empty */ 1070 activeQueues[d] = activeEntry; 1071 activeEntry->next = activeEntry->prev = activeEntry; 1072 } 1073} 1074 1075/* --- deactivate() --- 1076 * Remove an active node from the set of active nodes, and make it inactive. 1077 */ 1078void TriHeapExt::deactivate(TriHeapExtNode *node) 1079{ 1080 ActiveItem *activeEntry, *next, *prev, *first, *second; 1081 TriHeapExtNode *topActive; 1082 CandidateItem *candidate, *nextCandidate, *prevCandidate; 1083 size_t d; 1084 1085 /* Obtain pointers to the corresponding entry in the active node structure 1086 * and remove the node from the array of active nodes. 1087 */ 1088 1089 activeEntry = node->activeEntry; 1090 1091 size_t i = --activeCount; 1092 topActive = activeNodes[i]; 1093 activeNodes[activeEntry->position] = topActive; 1094 topActive->activeEntry->position = activeEntry->position; 1095 activeNodes[i] = std::nullptr_t (); 1096 1097 node->activeEntry = std::nullptr_t (); 1098 d = node->dim; 1099 first = activeQueues[d]; 1100 second = first->next; 1101 1102 /* Update the list according to the amount and position of existing list 1103 * items. This is a circular doubly linked list. 1104 */ 1105 if(second != first) { 1106 /* There are at least two items in the list. */ 1107 prev = activeEntry->prev; 1108 next = activeEntry->next; 1109 1110 /* May need to change pointer to first item. */ 1111 if(activeEntry == first) { 1112 activeQueues[d] = second; 1113 } 1114 1115 /* If there were only two nodes, we need to remove the candidate entry 1116 * from the candidates queue. 1117 */ 1118 if(second->next == first) { 1119 /* remove candidate. */ 1120 candidate = candidateItems[d]; 1121 candidateItems[d] = std::nullptr_t (); 1122 nextCandidate = candidate->next; 1123 prevCandidate = candidate->prev; 1124 1125 /* May need to change pointer to the first item. */ 1126 if(nextCandidate == candidate) { 1127 candQueueHead = std::nullptr_t (); 1128 } 1129 else { 1130 if(candQueueHead == candidate) { 1131 candQueueHead = nextCandidate; 1132 } 1133 prevCandidate->next = nextCandidate; 1134 nextCandidate->prev = prevCandidate; 1135 } 1136 1137 delete candidate; 1138 } 1139 1140 prev->next = next; 1141 next->prev = prev; 1142 } 1143 else { 1144 /* There is only one item in the list. */ 1145 activeQueues[d] = std::nullptr_t (); 1146 } 1147 1148 delete activeEntry; 1149} 1150 1151/* --- replaceActive() --- 1152 * Replaces one active node with another by simply updating the entry to point 1153 * to the other node. This is much quicker than performing deactivate() and 1154 * activate(). 1155 */ 1156void TriHeapExt::replaceActive( 1157 TriHeapExtNode *oldNode, TriHeapExtNode *newNode) 1158{ 1159 ActiveItem *activeEntry; 1160 1161 activeEntry = oldNode->activeEntry; 1162 newNode->activeEntry = activeEntry; 1163 oldNode->activeEntry = std::nullptr_t (); 1164 activeNodes[activeEntry->position] = newNode; 1165 1166 activeEntry->node = newNode; 1167} 1168 1169/*--- TriHeapExt (node manipulation methods) --------------------------------*/ 1170 1171/* --- merge() --- 1172 * Merges the two trunks pointed to by *a and *b, returning the sum trunk 1173 * through `a' and any carry tree through `b'. When this function is used, 1174 * both parameters `a' and `b' refer to either a 1-node or 2-node trunk. 1175 * 1176 * Returns the number of key comparisons used. 1177 */ 1178size_t TriHeapExt::merge(TriHeapExtNode **a, TriHeapExtNode **b) 1179{ 1180 TriHeapExtNode *tree, *nextTree, *other, *nextOther; 1181 size_t c; 1182 1183 /* Number of comparisons. */ 1184 c = 0; 1185 1186 /* `tree' always points to the node with the lowest key. 1187 * To begin with, `tree' points to the smaller head node, and `other' 1188 * points to the head node of the other trunk. 1189 */ 1190 if((*a)->key <= (*b)->key) { 1191 tree = (*a); 1192 other = (*b); 1193 } 1194 else { 1195 tree = (*b); 1196 other = (*a); 1197 } 1198 c++; 1199 1200 /* nextTree points to the next node on the trunk that `tree' is the head 1201 * of (if there is another node). 1202 * nextOther points to the next node on the trunk that `other' is the head 1203 * of (if there is another node). 1204 */ 1205 nextTree = tree->partner; 1206 nextOther = other->partner; 1207 1208 /* The merging depends on the existence of nodes and the values of keys. */ 1209 if(!nextTree) { 1210 /* nextTree does not exist, so we simply make `other' the child of 1211 * `tree'. If nextOther exist the resulting 3-node trunk is a carry 1212 * tree. 1213 */ 1214 1215 if(nextOther) { 1216 addChild(tree, other); 1217 tree->dim++; 1218 *a = std::nullptr_t (); 1219 *b = tree; 1220 } 1221 else { 1222 tree->partner = other; 1223 other->partner = tree; 1224 other->extra = TRUE; 1225 1226 *a = tree; 1227 *b = std::nullptr_t (); 1228 } 1229 } 1230 else if(!nextOther) { 1231 /* nextTree exists but nextOther does not, so the linked order of 1232 * nextTree and `other' in the resulting 3-node trunk depends on the 1233 * values of keys. The resulting 3-node trunk becomes a carry tree. 1234 */ 1235 1236 tree->partner = std::nullptr_t (); 1237 other->partner = nextTree; 1238 nextTree->partner = other; 1239 1240 if(other->key < nextTree->key) { 1241 addChild(tree, other); 1242 } 1243 else { 1244 nextTree->extra = FALSE; 1245 other->extra = TRUE; 1246 addChild(tree, nextTree); 1247 } 1248 1249 tree->dim++; 1250 1251 c++; 1252 *a = std::nullptr_t (); 1253 *b = tree; 1254 } 1255 else { 1256 /* Otherwise, both nextTree and nextOther exist. The result consists 1257 * of a 1 node trunk plus the 3-node trunk which becomes a carry tree. 1258 * We two trunks are made up as (tree, other, nextOther) 1259 * and (nextTree). This uses no key comparisons. 1260 */ 1261 1262 tree->partner = std::nullptr_t (); 1263 nextTree->partner = std::nullptr_t (); 1264 nextTree->extra = FALSE; 1265 nextTree->left = nextTree->right = nextTree; 1266 nextTree->parent = std::nullptr_t (); 1267 1268 addChild(tree, other); 1269 1270 tree->dim++; 1271 1272 *a = nextTree; *b = tree; 1273 } 1274 1275 return c; 1276} 1277 1278/* --- addChild() --- 1279 * Adds a new child node pointed to by $c$, to the parent node pointed to by 1280 * $p$. The user should ensure that the correct dimension child is being 1281 * added. 1282 */ 1283void TriHeapExt::addChild(TriHeapExtNode *p, TriHeapExtNode *c) 1284{ 1285 TriHeapExtNode *l, *r; 1286 1287 /* If p already has child nodes we must update the sibling pointers. 1288 * Otherwise only initialise the left and right pointers of the added 1289 * child. 1290 */ 1291 if((l = p->child)) { 1292 r = l->right; 1293 c->left = l; 1294 c->right = r; 1295 r->left = c; 1296 l->right = c; 1297 } 1298 else { 1299 c->left = c->right = c; 1300 } 1301 1302 p->child = c; 1303 c->parent = p; 1304} 1305 1306/* --- replaceChild() --- 1307 * Replaces child node pointed to by $old$ and its sub-tree with the child node 1308 * pointed to by $new$ and its sub-tree. 1309 */ 1310void TriHeapExt::replaceChild( 1311 TriHeapExtNode *oldNode, TriHeapExtNode *newNode) 1312{ 1313 TriHeapExtNode *parent, *l, *r; 1314 1315 r = oldNode->right; 1316 1317 /* If `oldNode' is an only child we only need to initialise the sibling 1318 * pointers of the new node. Otherwise we update sibling pointers of other 1319 * child nodes. 1320 */ 1321 if(r == oldNode) { 1322 newNode->right = newNode->left = newNode; 1323 } 1324 else { 1325 l = oldNode->left; 1326 l->right = newNode; 1327 r->left = newNode; 1328 newNode->left = l; 1329 newNode->right = r; 1330 } 1331 1332 /* Update parent pointer of the new node and possibly the child pointer 1333 * of the parent node. 1334 */ 1335 parent = oldNode->parent; 1336 newNode->parent = parent; 1337 if(parent->child == oldNode) parent->child = newNode; 1338} 1339 1340/*--- TriHeapExt (debugging) ------------------------------------------------*/ 1341 1342/* --- dumpNodes() ---- 1343 * Recursively print the nodes of a trinomial heap. 1344 */ 1345void TriHeapExt::dumpNodes(TriHeapExtNode *node, size_t level) 1346{ 1347 //#if SHOW_trih_ext 1348 TriHeapExtNode *childNode, *partner; 1349 size_t childCount; 1350 1351 /* Print leading whitespace for this level. */ 1352#if SHOW_trih_ext 1353 for (size_t i = 0; i < level; i++) 1354 Rcpp::Rcout << " "; 1355 1356 Rcpp::Rcout << node->item << "(" << node->key << ")"; 1357 Rcpp::Rcout.flush (); 1358 if(node->activeEntry) 1359 Rcpp::Rcout << '*'; 1360 Rcpp::Rcout << std::endl; 1361#endif 1362 1363 if((childNode = node->child)) { 1364 childNode = node->child->right; 1365 1366 childCount = 0; 1367 1368 do { 1369 dumpNodes(childNode, level+1); 1370 if(childNode->dim != childCount) { 1371 throw std::runtime_error ("error(dim)"); 1372 } 1373 if(childNode->parent != node) { 1374 throw std::runtime_error ("error(parent)"); 1375 } 1376 if(childNode->activeEntry == std::nullptr_t () && 1377 childNode->key < node->key) { 1378 throw std::runtime_error ("error(key)"); 1379 } 1380 childNode = childNode->right; 1381 childCount++; 1382 } while(childNode != node->child->right); 1383 1384 if(childCount != node->dim) { 1385 throw std::runtime_error ("error(childCount)"); 1386 } 1387 } 1388 else { 1389 if(node->dim != 0) { 1390 throw std::runtime_error ("error(dim)"); 1391 } 1392 } 1393 1394 if((partner=node->partner)) { 1395 if(node->extra==partner->extra) { 1396 throw std::runtime_error ("error(extra?)"); 1397 } 1398 if(partner->extra) { 1399 if(partner->dim != node->dim) { 1400 throw std::runtime_error ("error(dim)"); 1401 } 1402 if(partner->activeEntry && ! node->activeEntry) { 1403 throw std::runtime_error ("error(active)"); 1404 } 1405 1406 dumpNodes(partner, level); 1407 if(partner->key < node->key) { 1408 throw std::runtime_error ("error(key)"); 1409 } 1410 } 1411 } 1412 else if(node->parent) { 1413 throw std::runtime_error ("error(no partner)"); 1414 } 1415 1416 if(node->activeEntry) { 1417 if(node->activeEntry->node != node) { 1418 throw std::runtime_error ("error(active entry wrong)"); 1419 } 1420 } 1421 //#endif 1422} 1423 1424/* --- dump() --- 1425 * Print out a trinomial heap. 1426 */ 1427void TriHeapExt::dump() const 1428{ 1429#if SHOW_trih_ext 1430 TriHeapExtNode *node; 1431 TriHeapExtNode *firstChild; 1432 size_t c; 1433 1434 Rcpp::Rcout << std::endl << "value = " << treeSum << std::endl << 1435 "array entries 0..maxTrees ="; 1436 for (size_t i=0; i<maxTrees; i++) { 1437 bool is1or0 = trees [i] ? 1 : 0; 1438 Rcpp::Rcout << " " << is1or0; 1439 } 1440 Rcpp::Rcout << std::endl << "active nodes ="; 1441 for (size_t i=0; i<activeCount; i++) { 1442 if((node = activeNodes[i])) { 1443 Rcpp::Rcout << " " << node->item; 1444 firstChild = node->extra ? node->partner : node; 1445 if(!firstChild->parent) { 1446 throw std::runtime_error ("error(main trunk)"); 1447 } 1448 if(!node->activeEntry) { 1449 throw std::runtime_error ("error(inactive)"); 1450 } 1451 if(node->activeEntry->node != node) { 1452 throw std::runtime_error ("error(active entry wrong)"); 1453 } 1454 if(activeNodes[node->activeEntry->position] != node) { 1455 throw std::runtime_error ("error(active entry index wrong)"); 1456 } 1457 1458 c = 0; 1459 for(size_t j = i+1; j < activeCount; j++) { 1460 if(activeNodes[j] == node) c++; 1461 } 1462 if(c) { 1463 throw std::runtime_error ("error(repeated)"); 1464 } 1465 } 1466 else { 1467 throw std::runtime_error ("error(missing active mode)"); 1468 } 1469 } 1470 Rcpp::Rcout << std::endl << std::endl; 1471 for (size_t i=0; i<maxTrees; i++) { 1472 if((node = trees[i])) { 1473 Rcpp::Rcout << "tree " << i << std::endl << std::endl; 1474 dumpNodes(node, 0); 1475 Rcpp::Rcout << std::endl; 1476 } 1477 } 1478#endif 1479} 1480 1481/*---------------------------------------------------------------------------*/