Distances on Directed Graphs in R
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/*---------------------------------------------------------------------------*/