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

Generated by: LCOV version 2.5.0-beta