LCOV - code coverage report
Current view: top level - source/specialized/priority_queue.c (source / functions) Coverage Total Hit
Test: CCC Test Suite Coverage Report Lines: 97.1 % 306 297
Test Date: 2026-06-29 16:04:01 Functions: 100.0 % 33 33

            Line data    Source code
       1              : /** Copyright 2025 Alexander G. Lopez
       2              : 
       3              : Licensed under the Apache License, Version 2.0 (the "License");
       4              : you may not use this file except in compliance with the License.
       5              : You may obtain a copy of the License at
       6              : 
       7              :    http://www.apache.org/licenses/LICENSE-2.0
       8              : 
       9              : Unless required by applicable law or agreed to in writing, software
      10              : distributed under the License is distributed on an "AS IS" BASIS,
      11              : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      12              : See the License for the specific language governing permissions and
      13              : limitations under the License. */
      14              : /** C23 provided headers. */
      15              : #include <stddef.h>
      16              : 
      17              : /** CCC provided headers. */
      18              : #include "ccc/configuration.h" /* IWYU pragma: keep */
      19              : #include "ccc/specialized/priority_queue.h"
      20              : #include "ccc/specialized/private/private_priority_queue.h"
      21              : #include "ccc/types.h"
      22              : 
      23              : /*=========================  Function Prototypes   ==========================*/
      24              : 
      25              : static struct CCC_Priority_queue_node *
      26              : elem_in(struct CCC_Priority_queue const *, void const *);
      27              : static struct CCC_Priority_queue_node *merge(
      28              :     struct CCC_Priority_queue *,
      29              :     struct CCC_Priority_queue_node *,
      30              :     struct CCC_Priority_queue_node *
      31              : );
      32              : static void
      33              : link_child(struct CCC_Priority_queue_node *, struct CCC_Priority_queue_node *);
      34              : static void init_node(struct CCC_Priority_queue_node *);
      35              : static size_t traversal_count(struct CCC_Priority_queue_node const *);
      36              : static CCC_Tribool has_valid_links(
      37              :     struct CCC_Priority_queue const *,
      38              :     struct CCC_Priority_queue_node const *,
      39              :     struct CCC_Priority_queue_node const *
      40              : );
      41              : static struct CCC_Priority_queue_node *
      42              : delete_node(struct CCC_Priority_queue *, struct CCC_Priority_queue_node *);
      43              : static struct CCC_Priority_queue_node *
      44              : delete_root(struct CCC_Priority_queue *, struct CCC_Priority_queue_node *);
      45              : static void clear_node(struct CCC_Priority_queue_node *);
      46              : static void cut_child(struct CCC_Priority_queue_node *);
      47              : static void *struct_base(
      48              :     struct CCC_Priority_queue const *, struct CCC_Priority_queue_node const *
      49              : );
      50              : static CCC_Order order(
      51              :     struct CCC_Priority_queue const *,
      52              :     struct CCC_Priority_queue_node const *,
      53              :     struct CCC_Priority_queue_node const *
      54              : );
      55              : static void
      56              : update_fixup(struct CCC_Priority_queue *, struct CCC_Priority_queue_node *);
      57              : static void
      58              : increase_fixup(struct CCC_Priority_queue *, struct CCC_Priority_queue_node *);
      59              : static void
      60              : decrease_fixup(struct CCC_Priority_queue *, struct CCC_Priority_queue_node *);
      61              : 
      62              : /*=========================  Interface Functions   ==========================*/
      63              : 
      64              : void *
      65          614 : CCC_priority_queue_front(CCC_Priority_queue const *const priority_queue) {
      66          614 :     if (!priority_queue) {
      67            1 :         return NULL;
      68              :     }
      69          613 :     return priority_queue->root
      70          613 :              ? struct_base(priority_queue, priority_queue->root)
      71              :              : NULL;
      72          614 : }
      73              : 
      74              : void *
      75         2884 : CCC_priority_queue_push(
      76              :     CCC_Priority_queue *const priority_queue,
      77              :     CCC_Priority_queue_node *type_intruder,
      78              :     CCC_Allocator const *const allocator
      79              : ) {
      80         2884 :     if (!type_intruder || !priority_queue || !allocator) {
      81            3 :         return NULL;
      82              :     }
      83         2881 :     void *ret = struct_base(priority_queue, type_intruder);
      84         2881 :     if (allocator->allocate) {
      85        11476 :         void *const node = allocator->allocate((CCC_Allocator_arguments){
      86              :             .input = NULL,
      87         2869 :             .bytes = priority_queue->sizeof_type,
      88         2869 :             .alignment = priority_queue->alignof_type,
      89         2869 :             .context = allocator->context,
      90              :         });
      91         2869 :         if (!node) {
      92            1 :             return NULL;
      93              :         }
      94         2868 :         (void)memcpy(node, ret, priority_queue->sizeof_type);
      95         2868 :         ret = node;
      96         2868 :         type_intruder = elem_in(priority_queue, ret);
      97         2869 :     }
      98         2880 :     init_node(type_intruder);
      99         2880 :     priority_queue->root
     100         5760 :         = merge(priority_queue, priority_queue->root, type_intruder);
     101         2880 :     ++priority_queue->count;
     102         2880 :     return ret;
     103         2884 : }
     104              : 
     105              : CCC_Result
     106          706 : CCC_priority_queue_pop(
     107              :     CCC_Priority_queue *const priority_queue,
     108              :     CCC_Allocator const *const allocator
     109              : ) {
     110          706 :     if (!priority_queue || !priority_queue->root || !allocator) {
     111            2 :         return CCC_RESULT_ARGUMENT_ERROR;
     112              :     }
     113          704 :     struct CCC_Priority_queue_node *const popped = priority_queue->root;
     114          704 :     priority_queue->root = delete_root(priority_queue, priority_queue->root);
     115          704 :     priority_queue->count--;
     116          704 :     clear_node(popped);
     117          704 :     if (allocator->allocate) {
     118         2816 :         (void)allocator->allocate((CCC_Allocator_arguments){
     119          704 :             .input = struct_base(priority_queue, popped),
     120              :             .bytes = 0,
     121          704 :             .alignment = priority_queue->alignof_type,
     122          704 :             .context = allocator->context,
     123              :         });
     124          704 :     }
     125          704 :     return CCC_RESULT_OK;
     126          706 : }
     127              : 
     128              : void *
     129         1221 : CCC_priority_queue_extract(
     130              :     CCC_Priority_queue *const priority_queue,
     131              :     CCC_Priority_queue_node *const type_intruder
     132              : ) {
     133         1221 :     if (!priority_queue || !type_intruder || !priority_queue->root
     134         1220 :         || !type_intruder->next || !type_intruder->prev) {
     135            1 :         return NULL;
     136              :     }
     137         1220 :     priority_queue->root = delete_node(priority_queue, type_intruder);
     138         1220 :     priority_queue->count--;
     139         1220 :     clear_node(type_intruder);
     140         1220 :     return struct_base(priority_queue, type_intruder);
     141         1221 : }
     142              : 
     143              : CCC_Result
     144          102 : CCC_priority_queue_erase(
     145              :     CCC_Priority_queue *const priority_queue,
     146              :     CCC_Priority_queue_node *const type_intruder,
     147              :     CCC_Allocator const *const allocator
     148              : ) {
     149          102 :     if (!priority_queue || !type_intruder || !priority_queue->root || !allocator
     150          100 :         || !type_intruder->next || !type_intruder->prev) {
     151            2 :         return CCC_RESULT_ARGUMENT_ERROR;
     152              :     }
     153          100 :     priority_queue->root = delete_node(priority_queue, type_intruder);
     154          100 :     priority_queue->count--;
     155          100 :     if (allocator->allocate) {
     156          400 :         (void)allocator->allocate((CCC_Allocator_arguments){
     157          100 :             .input = struct_base(priority_queue, type_intruder),
     158              :             .bytes = 0,
     159          100 :             .alignment = priority_queue->alignof_type,
     160          100 :             .context = allocator->context,
     161              :         });
     162          100 :     }
     163          100 :     return CCC_RESULT_OK;
     164          102 : }
     165              : 
     166              : /** Deletes all nodes in the heap in linear time and constant space. This is
     167              : achieved by continually bringing up any child lists and splicing them into the
     168              : current child list being considered. We are avoiding recursion or amortized
     169              : O(log(N)) pops with this method. */
     170              : CCC_Result
     171            7 : CCC_priority_queue_clear(
     172              :     CCC_Priority_queue *const priority_queue,
     173              :     CCC_Destructor const *const destructor,
     174              :     CCC_Allocator const *const allocator
     175              : ) {
     176            7 :     if (!priority_queue || !destructor || !allocator) {
     177            3 :         return CCC_RESULT_ARGUMENT_ERROR;
     178              :     }
     179            4 :     struct CCC_Priority_queue_node *node = priority_queue->root;
     180          168 :     while (node) {
     181              :         /* The child and its siblings cut to the front of the line and we
     182              :            start again as if the child is the first in this sibling list. */
     183          164 :         if (node->child) {
     184           11 :             struct CCC_Priority_queue_node *const child = node->child;
     185           11 :             struct CCC_Priority_queue_node *const node_end = node->next;
     186              :             /* Final element of e child list pick up child as head */
     187           11 :             node_end->prev = child;
     188              :             /* Now e picks up the last (wrapping) element of child list. */
     189           11 :             node->next = child->next;
     190              :             /* Child has a list so don't just set child's prev to e. */
     191           11 :             child->next->prev = node;
     192              :             /* Child list wrapping element is now end of e list. */
     193           11 :             child->next = node_end;
     194              :             /* Our traversal now jumps to start of list we spliced in. */
     195           11 :             node->child = NULL;
     196           11 :             node = child;
     197              :             continue;
     198           11 :         }
     199              :         /* No more child lists to splice in so this node is done.  */
     200          306 :         struct CCC_Priority_queue_node *const prev_node
     201          153 :             = node->prev == node ? NULL : node->prev;
     202          153 :         node->next->prev = node->prev;
     203          153 :         node->prev->next = node->next;
     204          153 :         node->parent = node->next = node->prev = node->child = NULL;
     205          153 :         void *const destroy_this = struct_base(priority_queue, node);
     206          153 :         if (destructor->destroy) {
     207          150 :             destructor->destroy((CCC_Arguments){
     208           50 :                 .type = destroy_this,
     209           50 :                 .context = destructor->context,
     210              :             });
     211           50 :         }
     212          153 :         if (allocator->allocate) {
     213          612 :             (void)allocator->allocate((CCC_Allocator_arguments){
     214          153 :                 .input = destroy_this,
     215              :                 .bytes = 0,
     216          153 :                 .alignment = priority_queue->alignof_type,
     217          153 :                 .context = allocator->context,
     218              :             });
     219          153 :         }
     220          153 :         node = prev_node;
     221          153 :     }
     222            4 :     priority_queue->count = 0;
     223            4 :     priority_queue->root = NULL;
     224            4 :     return CCC_RESULT_OK;
     225            7 : }
     226              : 
     227              : CCC_Tribool
     228          618 : CCC_priority_queue_is_empty(CCC_Priority_queue const *const priority_queue) {
     229          618 :     if (!priority_queue) {
     230            1 :         return CCC_TRIBOOL_ERROR;
     231              :     }
     232          617 :     return !priority_queue->count;
     233          618 : }
     234              : 
     235              : CCC_Count
     236          548 : CCC_priority_queue_count(CCC_Priority_queue const *const priority_queue) {
     237          548 :     if (!priority_queue) {
     238            1 :         return (CCC_Count){.error = CCC_RESULT_ARGUMENT_ERROR};
     239              :     }
     240          547 :     return (CCC_Count){.count = priority_queue->count};
     241          548 : }
     242              : 
     243              : /** This is a difficult function. Without knowing if this new value is greater
     244              : or less than the previous we must always perform a delete and reinsert if the
     245              : value has not broken total order with the parent. It is not sufficient to check
     246              : if the value has exceeded the value of the first left child as any sibling of
     247              : that left child may be bigger than or smaller than that left child value. */
     248              : void *
     249           83 : CCC_priority_queue_update(
     250              :     CCC_Priority_queue *const priority_queue,
     251              :     CCC_Priority_queue_node *const type_intruder,
     252              :     CCC_Modifier const *const modifier
     253              : ) {
     254           83 :     if (!priority_queue || !type_intruder || !modifier || !modifier->modify
     255           80 :         || !type_intruder->next || !type_intruder->prev) {
     256            3 :         return NULL;
     257              :     }
     258          240 :     modifier->modify((CCC_Arguments){
     259           80 :         .type = struct_base(priority_queue, type_intruder),
     260           80 :         .context = modifier->context,
     261              :     });
     262           80 :     update_fixup(priority_queue, type_intruder);
     263           80 :     return struct_base(priority_queue, type_intruder);
     264           83 : }
     265              : 
     266              : /* Preferable to use this function if it is known the value is increasing.
     267              :    Much more efficient. */
     268              : void *
     269           57 : CCC_priority_queue_increase(
     270              :     CCC_Priority_queue *const priority_queue,
     271              :     CCC_Priority_queue_node *const type_intruder,
     272              :     CCC_Modifier const *const modifier
     273              : ) {
     274           57 :     if (!priority_queue || !type_intruder || !modifier || !modifier->modify
     275           54 :         || !type_intruder->next || !type_intruder->prev) {
     276            3 :         return NULL;
     277              :     }
     278          162 :     modifier->modify((CCC_Arguments){
     279           54 :         .type = struct_base(priority_queue, type_intruder),
     280           54 :         .context = modifier->context,
     281              :     });
     282           54 :     increase_fixup(priority_queue, type_intruder);
     283           54 :     return struct_base(priority_queue, type_intruder);
     284           57 : }
     285              : 
     286              : /* Preferable to use this function if it is known the value is decreasing.
     287              :    Much more efficient. */
     288              : void *
     289          154 : CCC_priority_queue_decrease(
     290              :     CCC_Priority_queue *const priority_queue,
     291              :     CCC_Priority_queue_node *const type_intruder,
     292              :     CCC_Modifier const *const modifier
     293              : ) {
     294          154 :     if (!priority_queue || !type_intruder || !modifier || !modifier->modify
     295          151 :         || !type_intruder->next || !type_intruder->prev) {
     296            3 :         return NULL;
     297              :     }
     298          453 :     modifier->modify((CCC_Arguments){
     299          151 :         .type = struct_base(priority_queue, type_intruder),
     300          151 :         .context = modifier->context,
     301              :     });
     302          151 :     decrease_fixup(priority_queue, type_intruder);
     303          151 :     return struct_base(priority_queue, type_intruder);
     304          154 : }
     305              : 
     306              : CCC_Tribool
     307         5300 : CCC_priority_queue_validate(CCC_Priority_queue const *const priority_queue) {
     308         5300 :     if (!priority_queue
     309         5300 :         || (priority_queue->root && priority_queue->root->parent)) {
     310            0 :         return CCC_FALSE;
     311              :     }
     312         5300 :     if (!has_valid_links(priority_queue, NULL, priority_queue->root)) {
     313            0 :         return CCC_FALSE;
     314              :     }
     315         5300 :     if (traversal_count(priority_queue->root) != priority_queue->count) {
     316            0 :         return CCC_FALSE;
     317              :     }
     318         5300 :     return CCC_TRUE;
     319         5300 : }
     320              : 
     321              : CCC_Order
     322            5 : CCC_priority_queue_order(CCC_Priority_queue const *const priority_queue) {
     323            5 :     return priority_queue ? priority_queue->order : CCC_ORDER_ERROR;
     324              : }
     325              : 
     326              : /*=========================  Private Interface     ==========================*/
     327              : 
     328              : void
     329            3 : CCC_private_priority_queue_push(
     330              :     struct CCC_Priority_queue *const priority_queue,
     331              :     struct CCC_Priority_queue_node *const node
     332              : ) {
     333            3 :     init_node(node);
     334            3 :     priority_queue->root = merge(priority_queue, priority_queue->root, node);
     335            3 :     ++priority_queue->count;
     336            3 : }
     337              : 
     338              : struct CCC_Priority_queue_node *
     339          272 : CCC_private_priority_queue_node_in(
     340              :     struct CCC_Priority_queue const *const priority_queue,
     341              :     void const *const any_struct
     342              : ) {
     343          272 :     return elem_in(priority_queue, any_struct);
     344              : }
     345              : 
     346              : void
     347           70 : CCC_private_priority_queue_update_fixup(
     348              :     struct CCC_Priority_queue *const pq,
     349              :     struct CCC_Priority_queue_node *const node
     350              : ) {
     351           70 :     return update_fixup(pq, node);
     352           70 : }
     353              : 
     354              : void
     355           51 : CCC_private_priority_queue_increase_fixup(
     356              :     struct CCC_Priority_queue *const pq,
     357              :     struct CCC_Priority_queue_node *const node
     358              : ) {
     359           51 :     return increase_fixup(pq, node);
     360           51 : }
     361              : 
     362              : void
     363          148 : CCC_private_priority_queue_decrease_fixup(
     364              :     struct CCC_Priority_queue *const pq,
     365              :     struct CCC_Priority_queue_node *const node
     366              : ) {
     367          148 :     return decrease_fixup(pq, node);
     368          148 : }
     369              : 
     370              : /*========================   Static Helpers  ================================*/
     371              : 
     372              : static void
     373          150 : update_fixup(
     374              :     struct CCC_Priority_queue *const priority_queue,
     375              :     struct CCC_Priority_queue_node *const node
     376              : ) {
     377              :     /* We could get lucky with a fast path but otherwise there is no way to
     378              :        know whether this is an increase or decrease and by how much. */
     379          150 :     if (node->parent
     380          150 :         && order(priority_queue, node, node->parent) == priority_queue->order) {
     381            2 :         cut_child(node);
     382            2 :     } else {
     383          148 :         priority_queue->root = delete_node(priority_queue, node);
     384          148 :         init_node(node);
     385              :     }
     386          150 :     priority_queue->root = merge(priority_queue, priority_queue->root, node);
     387          150 : }
     388              : 
     389              : static void
     390          105 : increase_fixup(
     391              :     struct CCC_Priority_queue *const priority_queue,
     392              :     struct CCC_Priority_queue_node *const node
     393              : ) {
     394          105 :     if (priority_queue->order == CCC_ORDER_GREATER
     395          105 :         && node == priority_queue->root) {
     396            1 :         return;
     397              :     }
     398          104 :     if (priority_queue->order == CCC_ORDER_GREATER) {
     399            1 :         cut_child(node);
     400            1 :     } else {
     401          103 :         priority_queue->root = delete_node(priority_queue, node);
     402          103 :         init_node(node);
     403              :     }
     404          104 :     priority_queue->root = merge(priority_queue, priority_queue->root, node);
     405          209 : }
     406              : 
     407              : static void
     408          299 : decrease_fixup(
     409              :     struct CCC_Priority_queue *const priority_queue,
     410              :     struct CCC_Priority_queue_node *const node
     411              : ) {
     412          299 :     if (priority_queue->order == CCC_ORDER_LESSER
     413          299 :         && node == priority_queue->root) {
     414            1 :         return;
     415              :     }
     416          298 :     if (priority_queue->order == CCC_ORDER_LESSER) {
     417          297 :         cut_child(node);
     418          297 :     } else {
     419            1 :         priority_queue->root = delete_node(priority_queue, node);
     420            1 :         init_node(node);
     421              :     }
     422          298 :     priority_queue->root = merge(priority_queue, priority_queue->root, node);
     423          597 : }
     424              : 
     425              : /** Cuts the child out of its current sibling list and redirects parent if
     426              : this child is directly pointed to by parent. The child is then made into its
     427              : own circular sibling list. The left child of this child, if one exists, is
     428              : still pointed to and not modified by this function. */
     429              : static void
     430         1738 : cut_child(struct CCC_Priority_queue_node *const child) {
     431         1738 :     child->next->prev = child->prev;
     432         1738 :     child->prev->next = child->next;
     433         1738 :     if (child->parent && child == child->parent->child) {
     434              :         /* To preserve the shuffle down properties the prev child should
     435              :            become the new child as that is the next youngest node. */
     436         1041 :         child->parent->child = child->prev == child ? NULL : child->prev;
     437         1041 :     }
     438         1738 :     child->parent = NULL;
     439         1738 :     child->next = child->prev = child;
     440         1738 : }
     441              : 
     442              : static struct CCC_Priority_queue_node *
     443         1572 : delete_node(
     444              :     struct CCC_Priority_queue *const priority_queue,
     445              :     struct CCC_Priority_queue_node *const root
     446              : ) {
     447         1572 :     if (priority_queue->root == root) {
     448          134 :         return delete_root(priority_queue, root);
     449              :     }
     450         1438 :     cut_child(root);
     451         1438 :     return merge(
     452         1438 :         priority_queue, priority_queue->root, delete_root(priority_queue, root)
     453              :     );
     454         1572 : }
     455              : 
     456              : /* Uses Fredman et al. oldest to youngest pairing method mentioned on pg 124
     457              : of the paper to pair nodes in one pass. Of all the variants for pairing given
     458              : in the paper this one is the back-to-front variant and the only one for which
     459              : the runtime analysis holds identically to the two-pass standard variant. A
     460              : non-trivial example for a heap.
     461              : 
     462              : < = next_sibling
     463              : > = prev_sibling
     464              : 
     465              :   ┌<1>┐
     466              :   └/──┘
     467              : ┌<9>─<1>─<9>─<7>─<8>┐
     468              : └───────────────────┘
     469              :         |
     470              :         v
     471              : ┌<9>─<1>─<7>─<8>┐
     472              : └────────/──────┘
     473              :       ┌<9>┐
     474              :       └───┘
     475              :         |
     476              :         v
     477              : ┌<9>─<1>─<7>┐
     478              : └────────/──┘
     479              :       ┌<8>─<9>┐
     480              :       └───────┘
     481              :         |
     482              :         v
     483              :   ┌<1>─<7>┐
     484              :   └/────/─┘
     485              : ┌<9>┐┌<8>─<9>┐
     486              : └───┘└───────┘
     487              :         |
     488              :         v
     489              :     ┌<1>┐
     490              :     └/──┘
     491              :   ┌<7>─<9>┐
     492              :   └/──────┘
     493              : ┌<8>─<9>┐
     494              : └───────┘
     495              : 
     496              : Delete root is the slowest operation offered by the priority queue and in
     497              : part contributes to the amortized `o(log(N))` runtime of the decrease key
     498              : operation. */
     499              : static struct CCC_Priority_queue_node *
     500         2276 : delete_root(
     501              :     struct CCC_Priority_queue *const priority_queue,
     502              :     struct CCC_Priority_queue_node *root
     503              : ) {
     504         2276 :     if (!root->child) {
     505          960 :         return NULL;
     506              :     }
     507         1316 :     struct CCC_Priority_queue_node *const eldest = root->child->next;
     508         1316 :     struct CCC_Priority_queue_node *accumulator = root->child->next;
     509         1316 :     struct CCC_Priority_queue_node *cur = root->child->next->next;
     510         4172 :     while (cur != eldest && cur->next != eldest) {
     511         2856 :         struct CCC_Priority_queue_node *const next = cur->next;
     512         2856 :         struct CCC_Priority_queue_node *const next_cur = cur->next->next;
     513         2856 :         next->next = next->prev = NULL;
     514         2856 :         cur->next = cur->prev = NULL;
     515              :         /* Double merge ensures `O(log(N))` steps rather than O(N). */
     516         2856 :         accumulator = merge(
     517         2856 :             priority_queue, accumulator, merge(priority_queue, cur, next)
     518              :         );
     519         2856 :         cur = next_cur;
     520         2856 :     }
     521              :     /* This covers the odd or even case for number of pairings. */
     522              :     root
     523         1316 :         = cur == eldest ? accumulator : merge(priority_queue, accumulator, cur);
     524              :     /* The root is always alone in its circular list at the end of merges. */
     525         1316 :     root->next = root->prev = root;
     526         1316 :     root->parent = NULL;
     527         1316 :     return root;
     528         2276 : }
     529              : 
     530              : /** Merges two priority queues, making the winner by ordering the root and
     531              : pushing the loser to the left child ring. Old should be the element that has
     532              : been in the queue longer and new, newer. This algorithm will still work if this
     533              : argument ordering is not respected and it does not change runtime, but it is how
     534              : to comply with the strategy outlined in the Fredman et. al. paper. */
     535              : static struct CCC_Priority_queue_node *
     536        11047 : merge(
     537              :     struct CCC_Priority_queue *const priority_queue,
     538              :     struct CCC_Priority_queue_node *const old,
     539              :     struct CCC_Priority_queue_node *const new
     540              : ) {
     541        11047 :     if (!old || !new || old == new) {
     542          979 :         return old ? old : new;
     543              :     }
     544        10068 :     if (order(priority_queue, new, old) == priority_queue->order) {
     545         1803 :         link_child(new, old);
     546         1803 :         return new;
     547              :     }
     548         8265 :     link_child(old, new);
     549         8265 :     return old;
     550        11047 : }
     551              : 
     552              : /* Oldest nodes shuffle down, new drops in to replace. This supports the
     553              : ring representation from Fredman et al., pg 125, fig 14 where the left
     554              : child's next pointer wraps to the last element in the list. The previous
     555              : pointer is to support faster deletes and decrease key operations.
     556              : 
     557              : < = next_sibling
     558              : > = prev_sibling
     559              : 
     560              :      A        A            A
     561              :     ╱        ╱            ╱
     562              : ┌─<B>─┐  ┌─<C>──<B>─┐ ┌─<D>──<C>──<B>─┐
     563              : └─────┘  └──────────┘ └───────────────┘
     564              : 
     565              : Pairing in the delete min phase would then start at B in this example and work
     566              : towards D. That is the oldest to youngest order mentioned in the paper and
     567              : helps set up the one-pass back-to-front variant mentioned in the paper allowing
     568              : the same runtime guarantees as the two pass standard pairing heap. */
     569              : static void
     570        10068 : link_child(
     571              :     struct CCC_Priority_queue_node *const parent,
     572              :     struct CCC_Priority_queue_node *const child
     573              : ) {
     574        10068 :     if (parent->child) {
     575         8233 :         child->next = parent->child->next;
     576         8233 :         child->prev = parent->child;
     577         8233 :         parent->child->next->prev = child;
     578         8233 :         parent->child->next = child;
     579         8233 :     } else {
     580         1835 :         child->next = child->prev = child;
     581              :     }
     582        10068 :     parent->child = child;
     583        10068 :     child->parent = parent;
     584        10068 : }
     585              : 
     586              : static inline CCC_Order
     587      1160666 : order(
     588              :     struct CCC_Priority_queue const *const priority_queue,
     589              :     struct CCC_Priority_queue_node const *const left,
     590              :     struct CCC_Priority_queue_node const *const right
     591              : ) {
     592      4642664 :     return priority_queue->comparator.compare((CCC_Comparator_arguments){
     593      1160666 :         .type_left = struct_base(priority_queue, left),
     594      1160666 :         .type_right = struct_base(priority_queue, right),
     595      1160666 :         .context = priority_queue->comparator.context,
     596              :     });
     597              : }
     598              : 
     599              : static inline void *
     600      2327573 : struct_base(
     601              :     struct CCC_Priority_queue const *const priority_queue,
     602              :     struct CCC_Priority_queue_node const *const node
     603              : ) {
     604      2327573 :     return ((char *)&(node->child)) - priority_queue->type_intruder_offset;
     605              : }
     606              : 
     607              : static inline struct CCC_Priority_queue_node *
     608         3140 : elem_in(
     609              :     struct CCC_Priority_queue const *const priority_queue,
     610              :     void const *const any_struct
     611              : ) {
     612         3140 :     return (struct CCC_Priority_queue_node
     613         3140 :                 *)((char *)any_struct + priority_queue->type_intruder_offset);
     614              : }
     615              : 
     616              : static inline void
     617         3135 : init_node(struct CCC_Priority_queue_node *const node) {
     618         3135 :     node->child = node->parent = NULL;
     619         3135 :     node->next = node->prev = node;
     620         3135 : }
     621              : 
     622              : static inline void
     623         1924 : clear_node(struct CCC_Priority_queue_node *const node) {
     624         1924 :     node->child = node->next = node->prev = node->parent = NULL;
     625         1924 : }
     626              : 
     627              : /*========================     Validation ================================*/
     628              : 
     629              : /* NOLINTBEGIN(*misc-no-recursion) */
     630              : 
     631              : static size_t
     632      1161025 : traversal_count(struct CCC_Priority_queue_node const *const root) {
     633      1161025 :     if (!root) {
     634       884803 :         return 0;
     635              :     }
     636       276222 :     size_t count = 0;
     637       276222 :     struct CCC_Priority_queue_node const *cur = root;
     638       276222 :     do {
     639      1155725 :         count += 1 + traversal_count(cur->child);
     640      1155725 :     } while ((cur = cur->next) != root);
     641       276222 :     return count;
     642      1161025 : }
     643              : 
     644              : static CCC_Tribool
     645      1161025 : has_valid_links(
     646              :     struct CCC_Priority_queue const *const priority_queue,
     647              :     struct CCC_Priority_queue_node const *const parent,
     648              :     struct CCC_Priority_queue_node const *const child
     649              : ) {
     650      1161025 :     if (!child) {
     651       884803 :         return CCC_TRUE;
     652              :     }
     653       276222 :     struct CCC_Priority_queue_node const *current = child;
     654       276222 :     CCC_Order const wrong_order = priority_queue->order == CCC_ORDER_LESSER
     655              :                                     ? CCC_ORDER_GREATER
     656              :                                     : CCC_ORDER_LESSER;
     657       276222 :     do {
     658              :         /* Reminder: Don't combine these if checks into one. Separating them
     659              :            makes it easier to find the problem when stepping through gdb. */
     660      1155725 :         if (!current) {
     661            0 :             return CCC_FALSE;
     662              :         }
     663      1155725 :         if (parent && child->parent != parent) {
     664            0 :             return CCC_FALSE;
     665              :         }
     666      1155725 :         if (parent && parent->child != child->parent->child) {
     667            0 :             return CCC_FALSE;
     668              :         }
     669      1155725 :         if (child->next->prev != child || child->prev->next != child) {
     670            0 :             return CCC_FALSE;
     671              :         }
     672      1155725 :         if (parent && (order(priority_queue, parent, current) == wrong_order)) {
     673            0 :             return CCC_FALSE;
     674              :         }
     675              :         /* RECURSE! */
     676      1155725 :         if (!has_valid_links(priority_queue, current, current->child)) {
     677            0 :             return CCC_FALSE;
     678              :         }
     679      1155725 :     } while ((current = current->next) != child);
     680       276222 :     return CCC_TRUE;
     681      1161025 : }
     682              : 
     683              : /* NOLINTEND(*misc-no-recursion) */
        

Generated by: LCOV version 2.4-beta