LCOV - code coverage report
Current view: top level - src/test - LG_heap.h (source / functions) Hit Total Coverage
Test: LAGraph code coverage report. Commit id: 3b461aa. Current time (UTC): 2024-01-25T16:04:32Z Lines: 78 78 100.0 %
Date: 2024-01-25 16:05:28 Functions: 5 5 100.0 %

          Line data    Source code
       1             : //------------------------------------------------------------------------------
       2             : // LG_heap: a Heap data structure and its operations
       3             : //------------------------------------------------------------------------------
       4             : 
       5             : // LAGraph, (c) 2019-2022 by The LAGraph Contributors, All Rights Reserved.
       6             : // SPDX-License-Identifier: BSD-2-Clause
       7             : //
       8             : // For additional details (including references to third party source code and
       9             : // other files) see the LICENSE file or contact permission@sei.cmu.edu. See
      10             : // Contributors.txt for a full list of contributors. Created, in part, with
      11             : // funding and support from the U.S. Government (see Acknowledgments.txt file).
      12             : // DM22-0790
      13             : 
      14             : // Contributed by Timothy A. Davis, Texas A&M University
      15             : 
      16             : //------------------------------------------------------------------------------
      17             : 
      18             : // The Heap is an array of LG_Elements: Heap [1..nheap].  Each entry in the
      19             : // Heap is a LG_Element, with a key and name.  LG_Element must be defined
      20             : // by the including file.  For example:
      21             : 
      22             : /*
      23             :     typedef int64_t LG_key_t ;
      24             :     typedef struct
      25             :     {
      26             :         int64_t name ;
      27             :         LG_key_t key ;
      28             :     }
      29             :     LG_Element ;
      30             :     #include "LG_heap.h"
      31             : */
      32             : 
      33             : #ifndef LG_HEAP_H
      34             : #define LG_HEAP_H
      35             : 
      36             : #undef  LG_FREE_ALL
      37             : #define LG_FREE_ALL ;
      38             : 
      39             : // These methods assume the caller allocates all memory, so no brutal memory
      40             : // test is needed.
      41             : 
      42             : //------------------------------------------------------------------------------
      43             : // LG_iheap_check: make sure Iheap is correct
      44             : //------------------------------------------------------------------------------
      45             : 
      46             : // Ensure that e == Heap [p] implies p == Iheap [e.name] for all entries
      47             : // in the heap.  Also ensure that e.name is in the range 0:n-1.
      48             : 
      49       10200 : static inline int LG_iheap_check
      50             : (
      51             :     // input:
      52             :     const LG_Element *restrict Heap,    // Heap [1..nheap], not modified
      53             :     const int64_t *restrict Iheap,      // Iheap [0..n-1], not modified
      54             :     const int64_t n,                    // element names are in range 0:n-1
      55             :     const int64_t nheap                 // the number of nodes in the Heap
      56             : )
      57             : {
      58             : 
      59       10200 :     char *msg = NULL ;
      60       10200 :     LG_ASSERT_MSG (Heap != NULL && Iheap != NULL && nheap >= 0 && n >= 0, -2000,
      61             :         "Heap is invalid") ;
      62             : 
      63             :     // check all entries in the heap
      64      225824 :     for (int64_t p = 1 ; p <= nheap ; p++)
      65             :     {
      66      215624 :         LG_Element e = Heap [p] ;
      67      215624 :         int64_t name = e.name ;
      68      215624 :         LG_ASSERT_MSG (name >= 0 && name < n && p == Iheap [name], -2000,
      69             :             "Heap is invalid") ;
      70             :     }
      71             : 
      72             :     // check all objects
      73      383307 :     for (int64_t name = 0 ; name < n ; name++)
      74             :     {
      75      373107 :         int64_t p = Iheap [name] ;
      76      373107 :         if (p <= 0)
      77             :         {
      78             :             // object with this name is not in the heap
      79             :         }
      80             :         else
      81             :         {
      82      215624 :             LG_ASSERT_MSG (p <= nheap, -2000, "position of object is invalid") ;
      83             :             // object with this name is in the heap at position p
      84      215624 :             LG_Element e = Heap [p] ;
      85      215624 :             LG_ASSERT_MSG (e.name == name, -2000, "Heap is invalid") ;
      86             :         }
      87             :     }
      88             : 
      89             :     // Heap and Iheap are consistent
      90       10200 :     return (GrB_SUCCESS) ;
      91             : }
      92             : 
      93             : //------------------------------------------------------------------------------
      94             : // LG_heap_check: make sure the min-heap property holds for the whole Heap
      95             : //------------------------------------------------------------------------------
      96             : 
      97             : // Check the entire Heap to see if it has the min-heap property:  for all nodes
      98             : // in the Heap, the key of a node must less than or equal to the keys of its
      99             : // children  (duplicate keys may appear).  An empty Heap or a Heap of size 1
     100             : // always satisfies the min-heap property, but nheap < 0 is invalid.  This
     101             : // function is for assertions only.
     102             : 
     103       10200 : static inline int LG_heap_check
     104             : (
     105             :     // input:
     106             :     const LG_Element *restrict Heap,    // Heap [1..nheap], not modified
     107             :     const int64_t *restrict Iheap,      // Iheap [0..n-1], not modified
     108             :     const int64_t n,                    // element names are in range 0:n-1
     109             :     const int64_t nheap                 // the number of nodes in the Heap
     110             : )
     111             : {
     112             : 
     113       10200 :     char *msg = NULL ;
     114       10200 :     LG_ASSERT_MSG (Heap != NULL && Iheap != NULL && nheap >= 0 && n >= 0, -2000,
     115             :         "Heap is invalid") ;
     116             : 
     117             : #if 0
     118             :     // dump the heap
     119             :     for (int64_t p = 1 ; p <= nheap ; p++)
     120             :     {
     121             :         printf ("Heap [%ld]: key %ld name: %ld\n", p, Heap [p].key,
     122             :             Heap [p].name) ;
     123             :         int64_t pleft  = 2*p ;          // left child of node p
     124             :         int64_t pright = pleft + 1 ;    // right child of node p
     125             :         if (pleft <= nheap)
     126             :         {
     127             :             printf ("     left  child: %ld (key %ld, name %ld)\n",
     128             :                 pleft, Heap [pleft].key, Heap [pleft].name) ;
     129             :         }
     130             :         if (pright <= nheap)
     131             :         {
     132             :             printf ("     right child: %ld (key %ld, name %ld)\n",
     133             :                 pright, Heap [pright].key, Heap [pright].name) ;
     134             :         }
     135             :         printf ("\n") ;
     136             :     }
     137             : #endif
     138             : 
     139             :     // nodes nheap/2 ... nheap have no children, so no need to check them
     140      115535 :     for (int64_t p = 1 ; p <= nheap / 2 ; p++)
     141             :     {
     142             : 
     143             :         // consider node p.  Its key must be <= the key of both its children.
     144             : 
     145      105335 :         int64_t pleft  = 2*p ;          // left child of node p
     146      105335 :         int64_t pright = pleft + 1 ;    // right child of node p
     147             : 
     148      105335 :         LG_ASSERT_MSG (! (pleft <= nheap && Heap [p].key > Heap [pleft].key),
     149             :             -2000, "the min-heap property is not satisfied") ;
     150             : 
     151      105335 :         LG_ASSERT_MSG (! (pright <= nheap && Heap [p].key > Heap [pright].key),
     152             :             -2000, "the min-heap property is not satisfied") ;
     153             :     }
     154             : 
     155             :     // Heap satisfies the min-heap property; also check Iheap
     156       10200 :     return (LG_iheap_check (Heap, Iheap, n, nheap)) ;
     157             : }
     158             : 
     159             : //------------------------------------------------------------------------------
     160             : // LG_heapify: enforce the min-heap property of a node
     161             : //------------------------------------------------------------------------------
     162             : 
     163             : // Heapify starting at node p in the Heap.  On input, the Heap rooted at node p
     164             : // satisfies the min-heap property, except for Heap [p] itself.  On output, all
     165             : // of the Heap rooted at node p satisfies the min-heap property.
     166             : 
     167       44092 : static inline void LG_heapify
     168             : (
     169             :     int64_t p,                      // node that needs to be heapified
     170             :     LG_Element *restrict Heap,      // Heap [1..nheap]
     171             :     int64_t *restrict Iheap,        // Iheap [0..n-1]
     172             :     const int64_t n,                // max element name
     173             :     const int64_t nheap             // the number of nodes in the Heap
     174             : )
     175             : {
     176             : 
     177             :     //--------------------------------------------------------------------------
     178             :     // check inputs and check for quick return
     179             :     //--------------------------------------------------------------------------
     180             : 
     181             :     ASSERT (Heap != NULL && Iheap != NULL) ;
     182             : 
     183       44092 :     if (p > nheap / 2 || nheap <= 1)
     184             :     {
     185             :         // nothing to do.  p has no children in the Heap.
     186             :         // Also safely do nothing if p is outside the Heap (p > nheap).
     187         622 :         return ;
     188             :     }
     189             : 
     190             :     //--------------------------------------------------------------------------
     191             :     // get the element to heapify
     192             :     //--------------------------------------------------------------------------
     193             : 
     194             :     // Get the element e at node p in the Heap; the one that needs heapifying.
     195       43470 :     LG_Element e = Heap [p] ;
     196             : 
     197             :     // There is now a "hole" at Heap [p], with no element in it.
     198             : 
     199             :     //--------------------------------------------------------------------------
     200             :     // heapify
     201             :     //--------------------------------------------------------------------------
     202             : 
     203             :     while (true)
     204      233225 :     {
     205             : 
     206             :         //----------------------------------------------------------------------
     207             :         // consider node p in the Heap
     208             :         //----------------------------------------------------------------------
     209             : 
     210             :         // Heap [p] is the "hole" in the Heap
     211             : 
     212      276695 :         int64_t pleft  = 2*p ;          // left child of node p
     213      276695 :         int64_t pright = pleft + 1 ;    // right child of node p
     214             : 
     215      276695 :         if (pright <= nheap)
     216             :         {
     217             : 
     218             :             //------------------------------------------------------------------
     219             :             // both left and right children are in the Heap
     220             :             //------------------------------------------------------------------
     221             : 
     222      267934 :             LG_Element eleft  = Heap [pleft] ;
     223      267934 :             LG_Element eright = Heap [pright] ;
     224      267934 :             if (eleft.key < eright.key)
     225             :             {
     226             :                 // left node has a smaller key than the right node
     227      119095 :                 if (e.key > eleft.key)
     228             :                 {
     229             :                     // key of element e is bigger than the left child of p, so
     230             :                     // bubble up the left child into the hole at Heap [p] and
     231             :                     // continue down the left child.  The hole moves to node
     232             :                     // pleft.
     233      118616 :                     Heap [p] = eleft ;
     234      118616 :                     Iheap [eleft.name] = p ;
     235      118616 :                     p = pleft ;
     236             :                 }
     237             :                 else
     238             :                 {
     239             :                     // done!  key of element e is is smaller than the left
     240             :                     // child of p; place e in the hole at p, and we're done.
     241         479 :                     Heap [p] = e ;
     242         479 :                     Iheap [e.name] = p ;
     243       34709 :                     return ;
     244             :                 }
     245             :             }
     246             :             else
     247             :             {
     248             :                 // right node has a smaller key than the left node
     249      148839 :                 if (e.key > eright.key)
     250             :                 {
     251             :                     // key of element e is bigger than the right child of p, so
     252             :                     // bubble up the right child into hole at Heap [p] and
     253             :                     // continue down the right child.  The hole moves to node
     254             :                     // pright.
     255      114609 :                     Heap [p] = eright ;
     256      114609 :                     Iheap [eright.name] = p ;
     257      114609 :                     p = pright ;
     258             :                 }
     259             :                 else
     260             :                 {
     261             :                     // done!  key of element e is is smaller than the right
     262             :                     // child of p; place e in the hole at p, and we're done.
     263       34230 :                     Heap [p] = e ;
     264       34230 :                     Iheap [e.name] = p ;
     265       34230 :                     return ;
     266             :                 }
     267             :             }
     268             :         }
     269             :         else
     270             :         {
     271             : 
     272             :             //------------------------------------------------------------------
     273             :             // right child is not in the Heap, see if left child is in the Heap
     274             :             //------------------------------------------------------------------
     275             : 
     276        8761 :             if (pleft <= nheap)
     277             :             {
     278             :                 // left child is in the Heap; check its key
     279        1185 :                 LG_Element eleft = Heap [pleft] ;
     280        1185 :                 if (e.key > eleft.key)
     281             :                 {
     282             :                     // key of element e is bigger than the left child of p, so
     283             :                     // bubble up the left child into the hole at Heap [p] and
     284             :                     // continue down the left child.  The hole moves to node
     285             :                     // pleft.
     286         289 :                     Heap [p] = eleft ;
     287         289 :                     Iheap [eleft.name] = p ;
     288         289 :                     p = pleft ;
     289             :                 }
     290             :             }
     291             : 
     292             :             //------------------------------------------------------------------
     293             :             // node p is a hole, and it has no children
     294             :             //------------------------------------------------------------------
     295             : 
     296             :             // put e in the hole, and we're done
     297        8761 :             Heap [p] = e ;
     298        8761 :             Iheap [e.name] = p ;
     299        8761 :             return ;
     300             :         }
     301             :     }
     302             : }
     303             : 
     304             : //------------------------------------------------------------------------------
     305             : // LG_heap_build: construct a Heap
     306             : //------------------------------------------------------------------------------
     307             : 
     308             : // On input, the Heap [1..nheap] may not satisfy the min-heap property, but
     309             : // Iheap must already be initialized.
     310             : // If e = Heap [p], then Iheap [e.name] = p must hold.
     311             : 
     312             : // On output, the elements have been rearranged so that it satisfies the
     313             : // heap property.
     314             : 
     315             : static inline void LG_heap_build
     316             : (
     317             :     LG_Element *restrict Heap,      // Heap [1..nheap]
     318             :     int64_t *restrict Iheap,        // Iheap [0..n-1]
     319             :     const int64_t n,                // max element name
     320             :     const int64_t nheap             // the number of nodes in the Heap
     321             : )
     322             : {
     323             : 
     324             :     //--------------------------------------------------------------------------
     325             :     // check inputs
     326             :     //--------------------------------------------------------------------------
     327             : 
     328             :     ASSERT (Heap != NULL && nheap >= 0) ;
     329             :     ASSERT (LG_iheap_check (Heap, Iheap, n, nheap)) ;
     330             : 
     331             :     //--------------------------------------------------------------------------
     332             :     // build the Heap
     333             :     //--------------------------------------------------------------------------
     334             : 
     335             :     for (int64_t p = nheap / 2 ; p >= 1 ; p--)
     336             :     {
     337             :         LG_heapify (p, Heap, Iheap, n, nheap) ;
     338             :     }
     339             : 
     340             :     //--------------------------------------------------------------------------
     341             :     // check result
     342             :     //--------------------------------------------------------------------------
     343             : 
     344             :     // Heap [1..nheap] now satisfies the min-heap property
     345             :     ASSERT (LG_heap_check (Heap, Iheap, n, nheap)) ;
     346             : }
     347             : 
     348             : //------------------------------------------------------------------------------
     349             : // LG_heap_delete: delete an element in the middle of a Heap
     350             : //------------------------------------------------------------------------------
     351             : 
     352       44092 : static inline void LG_heap_delete
     353             : (
     354             :     int64_t p,                      // node that needs to be deleted
     355             :     LG_Element *restrict Heap,      // Heap [1..nheap]
     356             :     int64_t *restrict Iheap,        // Iheap [0..n-1]
     357             :     const int64_t n,                // max element name
     358             :     int64_t *restrict nheap         // the number of nodes in the Heap;
     359             :                                     // decremented on output
     360             : )
     361             : {
     362             : 
     363             :     //--------------------------------------------------------------------------
     364             :     // check inputs
     365             :     //--------------------------------------------------------------------------
     366             : 
     367             :     ASSERT (Heap != NULL && Iheap != NULL && (*nheap) >= 0) ;
     368             :     ASSERT (p >= 1 && p <= (*nheap)) ;
     369             : 
     370             :     //--------------------------------------------------------------------------
     371             :     // delete node p from the Heap
     372             :     //--------------------------------------------------------------------------
     373             : 
     374             :     // move the last node to node p and decrement the # of nodes in the Heap
     375       44092 :     LG_Element elast = Heap [*nheap] ;  // last node in the heap
     376       44092 :     LG_Element edel  = Heap [p] ;       // element to delete from the heap
     377       44092 :     Heap [p] = elast ;              // move last node in the heap to position p
     378       44092 :     Iheap [elast.name] = p ;        // elast has been moved to position p
     379       44092 :     Iheap [edel.name] = 0 ;         // edel is no longer in the heap
     380       44092 :     (*nheap)-- ;                    // one less entry in the heap
     381             : 
     382             :     // heapify node p (safely does nothing if node p was the one just deleted)
     383       44092 :     LG_heapify (p, Heap, Iheap, n, (*nheap)) ;
     384       44092 : }
     385             : 
     386             : //------------------------------------------------------------------------------
     387             : // LG_heap_decrease_key: decrease the key of an entry in the heap
     388             : //------------------------------------------------------------------------------
     389             : 
     390       60199 : static inline void LG_heap_decrease_key
     391             : (
     392             :     int64_t p,                      // entry to modify in the heap
     393             :     const LG_key_t new_key,         // new key value of Heap [p]
     394             :     LG_Element *restrict Heap,      // Heap [1..nheap]
     395             :     int64_t *restrict Iheap,        // Iheap [0..n-1]
     396             :     const int64_t n,                // max element name
     397             :     const int64_t nheap             // the number of nodes in the Heap
     398             : )
     399             : {
     400             :     ASSERT (Heap != NULL && Iheap != NULL) ;
     401             :     ASSERT (p >= 1 && p < nheap) ;
     402             :     ASSERT (new_key < Heap [p].key) ;
     403             : 
     404             : //  printf ("Decreasing Heap [%ld] = name: %ld key: from %ld to %ld\n",
     405             : //      p, Heap [p].name, Heap [p].key, new_key) ;
     406             : 
     407       60199 :     Heap [p].key = new_key ;
     408       60199 :     int64_t parent = p/2 ;
     409             : 
     410      187582 :     while (p > 1 && Heap [parent].key > Heap [p].key)
     411             :     {
     412             :         // swap Heap [p] and Heap [parent]
     413             : //      printf ("swap positions %ld and %ld\n", p, parent) ;
     414             : 
     415      127383 :         LG_Element e = Heap [p] ;
     416      127383 :         Heap [p] = Heap [parent] ;
     417      127383 :         Heap [parent] = e ;
     418             : 
     419             :         // update the inverse heap
     420      127383 :         Iheap [Heap [p].name] = p ;
     421      127383 :         Iheap [Heap [parent].name] = parent ;
     422             : 
     423             :         // advance up to the parent
     424      127383 :         p = parent ;
     425      127383 :         parent = p/2 ;
     426             :     }
     427       60199 : }
     428             : 
     429             : #endif

Generated by: LCOV version 1.14