Blender V5.0
BLI_inplace_priority_queue.hh
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#pragma once
10
11#include "BLI_array.hh"
12#include "BLI_dot_export.hh"
13
14#include <sstream>
15
16namespace blender {
17
27template<
28 /* Type of the elements in the underlying array. */
29 typename T,
30 /* Binary function that takes two `const T &` inputs and returns true,
31 * when the first input has greater priority than the second. */
32 typename FirstHasHigherPriority = std::greater<T>>
34 private:
35 /* Underlying array the priority queue is built upon. This is a span instead of a mutable span,
36 * because this data structure never changes the values itself. */
37 Span<T> data_;
38 /* Maps indices from the heap (binary tree in array format) to indices of the underlying/original
39 * array. */
40 Array<int64_t> heap_to_orig_;
41 /* This is the inversion of the above mapping. */
42 Array<int64_t> orig_to_heap_;
43 /* Number of elements that are currently in the priority queue. */
44 int64_t heap_size_ = 0;
45 /* Function that can be changed to customize how the priority of two elements is compared. */
46 FirstHasHigherPriority first_has_higher_priority_fn_;
47
48 public:
53 : data_(data), heap_to_orig_(data_.size()), orig_to_heap_(data_.size())
54 {
55 for (const int64_t i : IndexRange(data_.size())) {
56 heap_to_orig_[i] = i;
57 orig_to_heap_[i] = i;
58 }
59
60 this->rebuild();
61 }
62
66 void rebuild()
67 {
68 const int final_heap_size = data_.size();
69 if (final_heap_size > 1) {
70 for (int64_t i = this->get_parent(final_heap_size - 1); i >= 0; i--) {
71 this->heapify(i, final_heap_size);
72 }
73 }
74 heap_size_ = final_heap_size;
75 }
76
81 int64_t size() const
82 {
83 return heap_size_;
84 }
85
90 bool is_empty() const
91 {
92 return heap_size_ == 0;
93 }
94
100 const T &peek() const
101 {
102 return data_[this->peek_index()];
103 }
104
110 const T &pop()
111 {
112 return data_[this->pop_index()];
113 }
114
119 {
120 BLI_assert(!this->is_empty());
121 return heap_to_orig_[0];
122 }
123
128 {
129 BLI_assert(!this->is_empty());
130 const int64_t top_index_orig = heap_to_orig_[0];
131 heap_size_--;
132 if (heap_size_ > 1) {
133 this->swap_indices(0, heap_size_);
134 this->heapify(0, heap_size_);
135 }
136 return top_index_orig;
137 }
138
143 void priority_decreased(const int64_t index)
144 {
145 const int64_t heap_index = orig_to_heap_[index];
146 if (heap_index >= heap_size_) {
147 /* This element is not in the queue currently. */
148 return;
149 }
150 this->heapify(heap_index, heap_size_);
151 }
152
157 void priority_increased(const int64_t index)
158 {
159 int64_t current = orig_to_heap_[index];
160 if (current >= heap_size_) {
161 /* This element is not in the queue currently. */
162 return;
163 }
164 while (true) {
165 if (current == 0) {
166 break;
167 }
168 const int64_t parent = this->get_parent(current);
169 if (this->first_has_higher_priority(parent, current)) {
170 break;
171 }
172 this->swap_indices(current, parent);
173 current = parent;
174 }
175 }
176
181 void priority_changed(const int64_t index)
182 {
183 this->priority_increased(index);
184 this->priority_decreased(index);
185 }
186
192 {
193 return heap_to_orig_.as_span().take_front(heap_size_);
194 }
195
202 {
203 return heap_to_orig_.as_span().drop_front(heap_size_);
204 }
205
210 {
211 return heap_to_orig_;
212 }
213
218 std::string to_dot() const
219 {
220 return this->partial_to_dot(heap_size_);
221 }
222
223 private:
224 bool first_has_higher_priority(const int64_t a, const int64_t b)
225 {
226 const T &value_a = data_[heap_to_orig_[a]];
227 const T &value_b = data_[heap_to_orig_[b]];
228 return first_has_higher_priority_fn_(value_a, value_b);
229 }
230
231 void swap_indices(const int64_t a, const int64_t b)
232 {
233 std::swap(heap_to_orig_[a], heap_to_orig_[b]);
234 orig_to_heap_[heap_to_orig_[a]] = a;
235 orig_to_heap_[heap_to_orig_[b]] = b;
236 }
237
238 void heapify(const int64_t parent, const int64_t heap_size)
239 {
240 int64_t max_index = parent;
241 const int left = this->get_left(parent);
242 const int right = this->get_right(parent);
243 if (left < heap_size && this->first_has_higher_priority(left, max_index)) {
244 max_index = left;
245 }
246 if (right < heap_size && this->first_has_higher_priority(right, max_index)) {
247 max_index = right;
248 }
249 if (max_index != parent) {
250 this->swap_indices(parent, max_index);
251 this->heapify(max_index, heap_size);
252 }
253 if (left < heap_size) {
254 BLI_assert(!this->first_has_higher_priority(left, parent));
255 }
256 if (right < heap_size) {
257 BLI_assert(!this->first_has_higher_priority(right, parent));
258 }
259 }
260
261 int64_t get_parent(const int64_t child) const
262 {
263 BLI_assert(child > 0);
264 return (child - 1) / 2;
265 }
266
267 int64_t get_left(const int64_t parent) const
268 {
269 return parent * 2 + 1;
270 }
271
272 int64_t get_right(const int64_t parent) const
273 {
274 return parent * 2 + 2;
275 }
276
277 std::string partial_to_dot(const int size) const
278 {
279 dot_export::DirectedGraph digraph;
280 Array<dot_export::Node *> dot_nodes(size);
281 for (const int i : IndexRange(size)) {
282 std::stringstream ss;
283 ss << data_[heap_to_orig_[i]];
284 const std::string name = ss.str();
285 dot_export::Node &node = digraph.new_node(name);
287 node.attributes.set("ordering", "out");
288 dot_nodes[i] = &node;
289 if (i > 0) {
290 const int64_t parent = this->get_parent(i);
291 digraph.new_edge(*dot_nodes[parent], node);
292 }
293 }
294 return digraph.to_dot_string();
295 }
296};
297
298} // namespace blender
#define BLI_assert(a)
Definition BLI_assert.h:46
BMesh const char void * data
long long int int64_t
constexpr Span drop_front(int64_t n) const
Definition BLI_span.hh:171
constexpr Span take_front(int64_t n) const
Definition BLI_span.hh:193
void priority_decreased(const int64_t index)
void priority_increased(const int64_t index)
void set(StringRef key, StringRef value)
std::string to_dot_string() const
DirectedEdge & new_edge(NodePort from, NodePort to)
Definition dot_export.cc:45
Node & new_node(StringRef label)
Definition dot_export.cc:20
void set_shape(Attr_shape shape)
static int left
#define T
const char * name
i
Definition text_draw.cc:230