Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Merge branch 'master' of scm.gforge.inria.fr:/gitroot/simgrid/simgrid
[simgrid.git] / src / xbt / heap.c
1 /* a generic and efficient heap                                             */
2
3 /* Copyright (c) 2004-2005, 2007-2015. The SimGrid Team.
4  * All rights reserved.                                                     */
5
6 /* This program is free software; you can redistribute it and/or modify it
7  * under the terms of the license (GNU LGPL) which comes with this package. */
8
9 #include "xbt/sysdep.h"
10 #include "xbt/log.h"
11 #include "heap_private.h"
12
13 #include <stdio.h>
14 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(xbt_heap, xbt, "Heap");
15
16 static void xbt_heap_max_heapify(xbt_heap_t H, int i);
17 static void xbt_heap_increase_key(xbt_heap_t H, int i);
18
19 /** @addtogroup XBT_heap
20  *  \brief This section describes the API to generic heap with O(log(n)) access.
21  */
22
23 /**
24  * @brief Creates a new heap.
25  * \param init_size initial size of the heap
26  * \param free_func function to call on each element when you want to free the whole heap (or NULL if nothing to do).
27  *
28  * Creates a new heap.
29  */
30 inline xbt_heap_t xbt_heap_new(int init_size, void_f_pvoid_t const free_func)
31 {
32   xbt_heap_t H = xbt_new0(struct xbt_heap, 1);
33   H->size = init_size;
34   H->count = 0;
35   H->items = (xbt_heap_item_t) xbt_new0(struct xbt_heap_item, init_size);
36   H->free = free_func;
37   return H;
38 }
39
40 /**
41  * @brief Set the update callback function.
42  * @param H the heap we're working on
43  * \param update_callback function to call on each element to update its index when needed.
44  */
45 inline void xbt_heap_set_update_callback(xbt_heap_t H, void (*update_callback) (void*, int))
46 {
47   H->update_callback = update_callback;
48 }
49
50 /**
51  * @brief kilkil a heap and its content
52  * @param H poor victim
53  */
54 void xbt_heap_free(xbt_heap_t H)
55 {
56   if (!H)
57     return;
58
59   if (H->free)
60     for (int i = 0; i < H->count; i++)
61       H->free(H->items[i].content);
62   free(H->items);
63   free(H);
64 }
65
66 /**
67  * @brief returns the number of elements in the heap
68  * @param H the heap we're working on
69  * @return the number of elements in the heap
70  */
71 inline int xbt_heap_size(xbt_heap_t H)
72 {
73   return (H->count);
74 }
75
76 /**
77  * @brief Add an element into the heap.
78  * \param H the heap we're working on
79  * \param content the object you want to add to the heap
80  * \param key the key associated to this object
81  *
82  * The element with the smallest key is automatically moved at the top of the heap.
83  */
84 void xbt_heap_push(xbt_heap_t H, void *content, double key)
85 {
86   int count = ++(H->count);
87
88   int size = H->size;
89   xbt_heap_item_t item;
90
91   if (count > size) {
92     H->size = (size << 1) + 1;
93     H->items = (void *) xbt_realloc(H->items, (H->size) * sizeof(struct xbt_heap_item));
94   }
95
96   item = &(H->items[count - 1]);
97   item->key = key;
98   item->content = content;
99   xbt_heap_increase_key(H, count - 1);
100   XBT_DEBUG("Heap has now %d elements and max elem is %g",xbt_heap_size(H),xbt_heap_maxkey(H));
101   return;
102 }
103
104 /**
105  * @brief Extracts from the heap and returns the element with the smallest key.
106  * \param H the heap we're working on
107  * \return the element with the smallest key
108  *
109  * Extracts from the heap and returns the element with the smallest key. The element with the next smallest key is
110  * automatically moved at the top of the heap.
111  */
112 void *xbt_heap_pop(xbt_heap_t H)
113 {
114   xbt_heap_item_t items = H->items;
115   int size = H->size;
116   void *max;
117
118   if (H->count == 0)
119     return NULL;
120
121   XBT_DEBUG("Heap has %d elements before extraction and max elem was %g",xbt_heap_size(H),xbt_heap_maxkey(H));
122
123   max = CONTENT(H, 0);
124
125   items[0] = items[(H->count) - 1];
126   (H->count)--;
127   xbt_heap_max_heapify(H,0);
128   if (H->count < size >> 2 && size > 16) {
129     size = (size >> 1) + 1;
130     H->items = (void *) xbt_realloc(items, size * sizeof(struct xbt_heap_item));
131     H->size = size;
132   }
133
134   if (H->update_callback)
135     H->update_callback(max, -1);
136   return max;
137 }
138
139 /**
140  * @brief Extracts from the heap and returns the element at position i.
141  * \param H the heap we're working on
142  * \param i  element position
143  * \return the element at position i if ok, NULL otherwise
144  *
145  * Extracts from the heap and returns the element at position i. The heap is automatically reordered.
146  */
147 void *xbt_heap_remove(xbt_heap_t H, int i)
148 {
149   XBT_DEBUG("Heap has %d elements: extracting element %d",xbt_heap_size(H),i);
150
151   if ((i < 0) || (i > H->count - 1))
152     return NULL;
153   /* put element i at head */
154   if (i > 0) {
155     KEY(H, i) = MIN_KEY_VALUE;
156     xbt_heap_increase_key(H, i);
157   }
158
159   return xbt_heap_pop(H);
160 }
161 /** @brief Remove an arbitrary element from the heap
162  *  @param H the heap we're working on
163  *  @param content the object you want to add to the heap
164  *  @param key the key associated to this object
165  */
166 void xbt_heap_rm_elm(xbt_heap_t H, void *content, double key) {
167   int i=0;
168   while (i < H->count && (KEY(H, i) != key || CONTENT(H, i) != content))
169     i++;
170   if (i == H->count)
171     return;
172   xbt_heap_remove(H,i);
173 }
174
175 /**
176  * @brief Updates an element of the heap with a new value.
177  * \param H the heap we're working on
178  * \param i  element position
179  * \param key new value for the element
180  *
181  * Updates an element of the heap with a new value.
182  */
183 void xbt_heap_update(xbt_heap_t H, int i, double key)
184 {
185   XBT_DEBUG("Heap has %d elements: updating element %d : was %1.12f to %1.12f ",xbt_heap_size(H),i,KEY(H, i), key);
186
187   if ((i < 0) || (i > H->count - 1) || key == KEY(H, i))
188     return ;
189
190   if(key< KEY(H, i)){
191     KEY(H, i)=key;
192     xbt_heap_increase_key(H, i);
193   }else{
194     KEY(H, i)=key;
195     xbt_heap_max_heapify(H,i);
196   }
197 }
198
199 /**
200  * @brief returns the smallest key in the heap (heap unchanged)
201  * \param H the heap we're working on
202  *
203  * \return the smallest key in the heap without modifying the heap.
204  */
205 inline double xbt_heap_maxkey(xbt_heap_t H)
206 {
207   xbt_assert(H->count != 0, "Empty heap");
208   return KEY(H, 0);
209 }
210
211 /**
212  * @brief returns the value associated to the smallest key in the heap (heap unchanged)
213  * \param H the heap we're working on
214  *
215  * \return the value associated to the smallest key in the heap
216  * without modifying the heap.
217  */
218 void *xbt_heap_maxcontent(xbt_heap_t H)
219 {
220   xbt_assert(H->count != 0, "Empty heap");
221   return CONTENT(H, 0);
222 }
223
224 /* <<<< private >>>>
225  * \param H the heap we're working on
226  *
227  * Restores the heap property once an element has been deleted.
228  */
229 static void xbt_heap_max_heapify(xbt_heap_t H, int index)
230 {
231   int i = index;
232   int count = H->count;
233   xbt_heap_item_t items = H->items;
234
235   while (1) {
236     int greatest = i;
237     int l = LEFT(i);
238     int r = l + 1;
239     if (l < count && items[l].key < items[i].key)
240       greatest = l;
241     if (r < count && items[r].key < items[greatest].key)
242       greatest = r;
243     if (greatest != i) {
244       struct xbt_heap_item tmp = items[i];
245       items[i] = items[greatest];
246       items[greatest] = tmp;
247       if (H->update_callback)
248         H->update_callback(items[i].content, i);
249       i = greatest;
250     } else {
251       if (H->update_callback)
252         H->update_callback(items[i].content, i);
253       return;
254     }
255   }
256 }
257
258 /* <<<< private >>>>
259  * \param H the heap we're working on
260  * \param i an item position in the heap
261  *
262  * Moves up an item at position i to its correct position. Works only when called from xbt_heap_push.
263  * Do not use otherwise.
264  */
265 static void xbt_heap_increase_key(xbt_heap_t H, int i)
266 {
267   xbt_heap_item_t items = H->items;
268   int p = PARENT(i);
269   while (i > 0 && items[p].key > items[i].key) {
270     struct xbt_heap_item tmp = items[i];
271     items[i] = items[p];
272     items[p] = tmp;
273     if (H->update_callback)
274       H->update_callback(items[i].content, i);
275     i = p;
276     p = PARENT(i);
277   }
278   if (H->update_callback)
279     H->update_callback(items[i].content, i);
280   return;
281 }