Blender V4.3
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
5#pragma once
6
7#include "BLI_array.hh"
8#include "BLI_dot_export.hh"
9
10#include <sstream>
11
12namespace blender {
13
23template<
24 /* Type of the elements in the underlying array. */
25 typename T,
26 /* Binary function that takes two `const T &` inputs and returns true,
27 * when the first input has greater priority than the second. */
28 typename FirstHasHigherPriority = std::greater<T>>
30 private:
31 /* Underlying array the priority queue is built upon. This is a span instead of a mutable span,
32 * because this data structure never changes the values itself. */
33 Span<T> data_;
34 /* Maps indices from the heap (binary tree in array format) to indices of the underlying/original
35 * array. */
36 Array<int64_t> heap_to_orig_;
37 /* This is the inversion of the above mapping. */
38 Array<int64_t> orig_to_heap_;
39 /* Number of elements that are currently in the priority queue. */
40 int64_t heap_size_ = 0;
41 /* Function that can be changed to customize how the priority of two elements is compared. */
42 FirstHasHigherPriority first_has_higher_priority_fn_;
43
44 public:
49 : data_(data), heap_to_orig_(data_.size()), orig_to_heap_(data_.size())
50 {
51 for (const int64_t i : IndexRange(data_.size())) {
52 heap_to_orig_[i] = i;
53 orig_to_heap_[i] = i;
54 }
55
56 this->rebuild();
57 }
58
62 void rebuild()
63 {
64 const int final_heap_size = data_.size();
65 if (final_heap_size > 1) {
66 for (int64_t i = this->get_parent(final_heap_size - 1); i >= 0; i--) {
67 this->heapify(i, final_heap_size);
68 }
69 }
70 heap_size_ = final_heap_size;
71 }
72
77 int64_t size() const
78 {
79 return heap_size_;
80 }
81
86 bool is_empty() const
87 {
88 return heap_size_ == 0;
89 }
90
96 const T &peek() const
97 {
98 return data_[this->peek_index()];
99 }
100
106 const T &pop()
107 {
108 return data_[this->pop_index()];
109 }
110
115 {
116 BLI_assert(!this->is_empty());
117 return heap_to_orig_[0];
118 }
119
124 {
125 BLI_assert(!this->is_empty());
126 const int64_t top_index_orig = heap_to_orig_[0];
127 heap_size_--;
128 if (heap_size_ > 1) {
129 this->swap_indices(0, heap_size_);
130 this->heapify(0, heap_size_);
131 }
132 return top_index_orig;
133 }
134
139 void priority_decreased(const int64_t index)
140 {
141 const int64_t heap_index = orig_to_heap_[index];
142 if (heap_index >= heap_size_) {
143 /* This element is not in the queue currently. */
144 return;
145 }
146 this->heapify(heap_index, heap_size_);
147 }
148
153 void priority_increased(const int64_t index)
154 {
155 int64_t current = orig_to_heap_[index];
156 if (current >= heap_size_) {
157 /* This element is not in the queue currently. */
158 return;
159 }
160 while (true) {
161 if (current == 0) {
162 break;
163 }
164 const int64_t parent = this->get_parent(current);
165 if (this->first_has_higher_priority(parent, current)) {
166 break;
167 }
168 this->swap_indices(current, parent);
169 current = parent;
170 }
171 }
172
177 void priority_changed(const int64_t index)
178 {
179 this->priority_increased(index);
180 this->priority_decreased(index);
181 }
182
188 {
189 return heap_to_orig_.as_span().take_front(heap_size_);
190 }
191
198 {
199 return heap_to_orig_.as_span().drop_front(heap_size_);
200 }
201
206 {
207 return heap_to_orig_;
208 }
209
214 std::string to_dot() const
215 {
216 return this->partial_to_dot(heap_size_);
217 }
218
219 private:
220 bool first_has_higher_priority(const int64_t a, const int64_t b)
221 {
222 const T &value_a = data_[heap_to_orig_[a]];
223 const T &value_b = data_[heap_to_orig_[b]];
224 return first_has_higher_priority_fn_(value_a, value_b);
225 }
226
227 void swap_indices(const int64_t a, const int64_t b)
228 {
229 std::swap(heap_to_orig_[a], heap_to_orig_[b]);
230 orig_to_heap_[heap_to_orig_[a]] = a;
231 orig_to_heap_[heap_to_orig_[b]] = b;
232 }
233
234 void heapify(const int64_t parent, const int64_t heap_size)
235 {
236 int64_t max_index = parent;
237 const int left = this->get_left(parent);
238 const int right = this->get_right(parent);
239 if (left < heap_size && this->first_has_higher_priority(left, max_index)) {
240 max_index = left;
241 }
242 if (right < heap_size && this->first_has_higher_priority(right, max_index)) {
243 max_index = right;
244 }
245 if (max_index != parent) {
246 this->swap_indices(parent, max_index);
247 this->heapify(max_index, heap_size);
248 }
249 if (left < heap_size) {
250 BLI_assert(!this->first_has_higher_priority(left, parent));
251 }
252 if (right < heap_size) {
253 BLI_assert(!this->first_has_higher_priority(right, parent));
254 }
255 }
256
257 int64_t get_parent(const int64_t child) const
258 {
259 BLI_assert(child > 0);
260 return (child - 1) / 2;
261 }
262
263 int64_t get_left(const int64_t parent) const
264 {
265 return parent * 2 + 1;
266 }
267
268 int64_t get_right(const int64_t parent) const
269 {
270 return parent * 2 + 2;
271 }
272
273 std::string partial_to_dot(const int size) const
274 {
275 dot::DirectedGraph digraph;
276 Array<dot::Node *> dot_nodes(size);
277 for (const int i : IndexRange(size)) {
278 std::stringstream ss;
279 ss << data_[heap_to_orig_[i]];
280 const std::string name = ss.str();
281 dot::Node &node = digraph.new_node(name);
282 node.set_shape(dot::Attr_shape::Rectangle);
283 node.attributes.set("ordering", "out");
284 dot_nodes[i] = &node;
285 if (i > 0) {
286 const int64_t parent = this->get_parent(i);
287 digraph.new_edge(*dot_nodes[parent], node);
288 }
289 }
290 return digraph.to_dot_string();
291 }
292};
293
294} // namespace blender
#define BLI_assert(a)
Definition BLI_assert.h:50
Span< T > as_span() const
Definition BLI_array.hh:232
void priority_decreased(const int64_t index)
void priority_increased(const int64_t index)
constexpr int64_t size() const
Definition BLI_span.hh:253
DirectedEdge & new_edge(NodePort from, NodePort to)
Definition dot_export.cc:41
std::string to_dot_string() const
Node & new_node(StringRef label)
Definition dot_export.cc:16
void set_shape(Attr_shape shape)
local_group_size(16, 16) .push_constant(Type b
OperationNode * node
static int left
#define T
__int64 int64_t
Definition stdint.h:89