GFXprim
2D bitmap graphics library with emphasis on speed and correctness
Loading...
Searching...
No Matches
gp_heap.h
Go to the documentation of this file.
1// SPDX-License-Identifier: LGPL-2.1-or-later
2/*
3 * Copyright (C) 2009-2023 Cyril Hrubis <metan@ucw.cz>
4 */
5
11#ifndef UTILS_GP_HEAP_H
12#define UTILS_GP_HEAP_H
13
14#include <string.h>
15#include <core/gp_debug.h>
16#include <core/gp_common.h>
17#include <core/gp_compiler.h>
18#include <utils/gp_types.h>
19
20#define GP_HEAP_ENTRY(ptr, structure, member) \
21 GP_CONTAINER_OF(ptr, structure, member)
22
23static inline unsigned long gp_heap_size(gp_heap_head *heap)
24{
25 return heap ? heap->children + 1 : 0;
26}
27
28/*
29 * If needed fixes up->left or up->right
30 */
31static inline void gp_heap_fix_up(gp_heap_head *up, gp_heap_head *old, gp_heap_head *rep)
32{
33 if (!up)
34 return;
35
36 if (old == up->left)
37 up->left = rep;
38 else if (old == up->right)
39 up->right = rep;
40 else
41 GP_BUG("Invalid heap state");
42}
43
44/*
45 * If needed fixes elem->left->up and elem->right->up
46 */
47static inline void gp_heap_fix_up_lr(gp_heap_head *elem, gp_heap_head *up)
48{
49 if (elem->left)
50 elem->left->up = up;
51
52 if (elem->right)
53 elem->right->up = up;
54}
55
56/*
57 * Swaps left child with its parent.
58 */
59static inline gp_heap_head *gp_heap_swap_left(gp_heap_head *heap)
60{
61 gp_heap_head *left = heap->left;
62
63 gp_heap_fix_up(heap->up, heap, left);
64
65 left->up = heap->up;
66 heap->up = left;
67
68 if (heap->right)
69 heap->right->up = left;
70
71 gp_heap_fix_up_lr(left, heap);
72
73 heap->left = left->left;
74
75 if (left)
76 left->left = heap;
77
78 GP_SWAP(heap->right, left->right);
79 GP_SWAP(heap->children, left->children);
80
81 return left;
82}
83
84/*
85 * Swaps right child with its parent.
86 */
87static inline gp_heap_head *gp_heap_swap_right(gp_heap_head *heap)
88{
89 gp_heap_head *right = heap->right;
90 gp_heap_fix_up(heap->up, heap, right);
91
92 right->up = heap->up;
93 heap->up = right;
94
95 if (heap->left)
96 heap->left->up = right;
97
98 gp_heap_fix_up_lr(right, heap);
99
100 heap->right = right->right;
101 right->right = heap;
102
103 GP_SWAP(heap->left, right->left);
104 GP_SWAP(heap->children, right->children);
105
106 return right;
107}
108
109/*
110 * Returns true if subtree is well balanced i.e. we have 2^n - 2 children
111 */
112static int gp_heap_well_balanced(unsigned int children)
113{
114 return !((children + 2) & (children + 1));
115}
116
117/*
118 * Inserts an element into a heap.
119 */
120GP_WUR gp_heap_head *gp_heap_ins_(gp_heap_head *heap, gp_heap_head *parent, gp_heap_head *elem,
121 int (*cmp)(gp_heap_head *e1, gp_heap_head *e2))
122{
123 if (!heap) {
124 memset(elem, 0, sizeof(*elem));
125 elem->up = parent;
126 return elem;
127 }
128
129 heap->children++;
130
131 if (!heap->left || !gp_heap_well_balanced(heap->left->children) ||
132 (heap->right && heap->left->children == heap->right->children)) {
133
134 heap->left = gp_heap_ins_(heap->left, heap, elem, cmp);
135
136 if (cmp(heap, heap->left))
137 return gp_heap_swap_left(heap);
138 } else {
139
140 heap->right = gp_heap_ins_(heap->right, heap, elem, cmp);
141
142 if (cmp(heap, heap->right))
143 return gp_heap_swap_right(heap);
144 }
145
146 return heap;
147}
148
157GP_WUR gp_heap_head *gp_heap_ins(gp_heap_head *heap, gp_heap_head *elem,
158 int (*cmp)(gp_heap_head *e1, gp_heap_head *e2))
159{
160 return gp_heap_ins_(heap, NULL, elem, cmp);
161}
162
172GP_WUR static inline gp_heap_head *gp_heap_rem_last(gp_heap_head *heap,
173 gp_heap_head **last)
174{
175 if (!heap->left) {
176 *last = heap;
177 return NULL;
178 }
179
180 if (!gp_heap_well_balanced(heap->left->children) ||
181 !heap->right || (heap->right->children < heap->left->children/2)) {
182 heap->left = gp_heap_rem_last(heap->left, last);
183 } else {
184 heap->right = gp_heap_rem_last(heap->right, last);
185 }
186
187 heap->children--;
188
189 return heap;
190}
191
192static inline gp_heap_head *gp_heap_bubble_down(gp_heap_head *heap,
193 int (*cmp)(gp_heap_head *e1, gp_heap_head *e2))
194{
195 gp_heap_head *right = heap->right;
196 gp_heap_head *left = heap->left;
197
198 /* Make sure we choose smaller one */
199 if (right && left && cmp(right, left))
200 right = NULL;
201
202 if (right && cmp(heap, right)) {
203 gp_heap_swap_right(heap);
204 right->right = gp_heap_bubble_down(heap, cmp);
205 return right;
206 }
207
208 if (left && cmp(heap, left)) {
209 gp_heap_swap_left(heap);
210 left->left = gp_heap_bubble_down(heap, cmp);
211 return left;
212 }
213
214 return heap;
215}
216
223GP_WUR static inline gp_heap_head *gp_heap_pop(gp_heap_head *heap,
224 int (*cmp)(gp_heap_head *e1, gp_heap_head *e2))
225{
226 gp_heap_head *last;
227
228 if (!heap)
229 return NULL;
230
231 heap = gp_heap_rem_last(heap, &last);
232 if (!heap)
233 return NULL;
234
235 last->left = heap->left;
236 last->right = heap->right;
237 last->children = heap->children;
238 last->up = NULL;
239
240 gp_heap_fix_up_lr(last, last);
241
242 return gp_heap_bubble_down(last, cmp);
243}
244
245/*
246 * Moves replacement element up if needed.
247 */
248GP_WUR static inline gp_heap_head *gp_heap_bubble_up(gp_heap_head *heap,
249 int (*cmp)(gp_heap_head *e1, gp_heap_head *e2))
250{
251 if (!heap || !heap->up)
252 return heap;
253
254 if (cmp(heap, heap->up))
255 return heap;
256
257 gp_heap_head *up = heap->up;
258
259 if (up->left == heap)
260 up = gp_heap_swap_left(up);
261 else if (up->right == heap)
262 up = gp_heap_swap_right(up);
263 else
264 GP_BUG("Invalid heap state");
265
266 return gp_heap_bubble_up(up, cmp);
267}
268
280GP_WUR static inline gp_heap_head *gp_heap_rem(gp_heap_head *heap, gp_heap_head *elem,
281 int (*cmp)(gp_heap_head *e1, gp_heap_head *e2))
282{
283 gp_heap_head *last;
284
285 /* Heap is empty */
286 if (!heap)
287 return NULL;
288
289 heap = gp_heap_rem_last(heap, &last);
290
291 /* We found it already */
292 if (elem == last)
293 return heap;
294
295 /* Replace removed element with last */
296 last->left = elem->left;
297 last->right = elem->right;
298 last->children = elem->children;
299 last->up = elem->up;
300
301 gp_heap_fix_up(elem->up, elem, last);
302 gp_heap_fix_up_lr(last, last);
303
304 last = gp_heap_bubble_up(last, cmp);
305 last = gp_heap_bubble_down(last, cmp);
306
307 while (last->up)
308 last = last->up;
309
310 return last;
311}
312
313#endif /* UTILS_GP_HEAP_H */
Common macros.
#define GP_SWAP(a, b)
Swaps a and b.
Definition gp_common.h:124
A compiler dependent macros.
#define GP_WUR
Expands to warn_unused_result attribute when supported by the compiler.
Definition gp_compiler.h:26
A debug message layer.
#define GP_BUG(...)
A debug BUG printf-like macro.
Definition gp_debug.h:112
static gp_heap_head * gp_heap_pop(gp_heap_head *heap, int(*cmp)(gp_heap_head *e1, gp_heap_head *e2))
Removes top element from the heap.
Definition gp_heap.h:223
static gp_heap_head * gp_heap_rem_last(gp_heap_head *heap, gp_heap_head **last)
Removes last element on the last level.
Definition gp_heap.h:172
gp_heap_head * gp_heap_ins(gp_heap_head *heap, gp_heap_head *elem, int(*cmp)(gp_heap_head *e1, gp_heap_head *e2))
Inserts a node into a heap.
Definition gp_heap.h:157
static gp_heap_head * gp_heap_rem(gp_heap_head *heap, gp_heap_head *elem, int(*cmp)(gp_heap_head *e1, gp_heap_head *e2))
Removes element from a heap.
Definition gp_heap.h:280
Common header for types.