Skip to content

Commit 6e021ac

Browse files
authored
Priority Queue (#33)
* started pqueue * extended queue types * wrote tests * tiny optimization
1 parent 819e5ee commit 6e021ac

File tree

3 files changed

+665
-0
lines changed

3 files changed

+665
-0
lines changed
Lines changed: 349 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,349 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.4;
3+
4+
/**
5+
* @notice The library that realizes a heap based priority queue.
6+
*
7+
* Courtesy of heap property,
8+
* add(), remove(), and removeTop() operations are O(log(n)) complex
9+
* top() operation is O(1)
10+
*
11+
* The library might be useful to implement priority withdrawals/purchases, reputation based systems, and similar logic.
12+
*
13+
* The library is a maximal priority queue. The element with the highest priority is the topmost element.
14+
* If you wish a minimal queue, change the priority of the elements to type(uint256).max - priority.
15+
*
16+
* Note the queue order of the elements with the same priority is not guaranteed.
17+
*
18+
* Usage example:
19+
*
20+
* using PriorityQueue for PriorityQueue.UintQueue;
21+
* using PriorityQueue for PriorityQueue.AddressQueue;
22+
* using PriorityQueue for PriorityQueue.Bytes32Queue;
23+
*/
24+
library PriorityQueue {
25+
/**
26+
************************
27+
* UintQueue *
28+
************************
29+
*/
30+
31+
struct UintQueue {
32+
Queue _queue;
33+
}
34+
35+
/**
36+
* @notice The function to add an element to the queue. O(log(n)) complex
37+
* @param queue self
38+
* @param value_ the element value
39+
* @param priority_ the element priority
40+
*/
41+
function add(UintQueue storage queue, uint256 value_, uint256 priority_) internal {
42+
_add(queue._queue, bytes32(value_), priority_);
43+
}
44+
45+
/**
46+
* @notice The function to remove an element from the queue by index. O(log(n)) complex
47+
* @param queue self
48+
* @param index_ the index of the element to remove
49+
*/
50+
function remove(UintQueue storage queue, uint256 index_) internal {
51+
_remove(queue._queue, index_);
52+
}
53+
54+
/**
55+
* @notice The function to remove the element with the highest priority. O(log(n)) complex
56+
* @param queue self
57+
*/
58+
function removeTop(UintQueue storage queue) internal {
59+
_removeTop(queue._queue);
60+
}
61+
62+
/**
63+
* @notice The function to read the element with the highest priority. O(1) complex
64+
* @param queue self
65+
* @return the element with the highest priority
66+
*/
67+
function top(UintQueue storage queue) internal view returns (uint256) {
68+
return uint256(_top(queue._queue));
69+
}
70+
71+
/**
72+
* @notice The function to read the size of the queue. O(1) complex
73+
* @param queue self
74+
* @return the size of the queue
75+
*/
76+
function length(UintQueue storage queue) internal view returns (uint256) {
77+
return _length(queue._queue);
78+
}
79+
80+
/**
81+
* @notice The function to read the element of the queue by index. O(1) complex
82+
* @param queue self
83+
* @param index_ the index of the element to read
84+
* @return the value and the priority of the element
85+
*/
86+
function at(UintQueue storage queue, uint256 index_) internal view returns (uint256, uint256) {
87+
(bytes32 value_, uint256 priority_) = _at(queue._queue, index_);
88+
89+
return (uint256(value_), priority_);
90+
}
91+
92+
/**
93+
* @notice The function to get the values and priorities stored in the queue. O(n) complex
94+
* It is very expensive to call this function as it reads all the queue elements. Use cautiously
95+
* @param queue self
96+
* @return values_ the values of the elements stored
97+
* @return priorities_ the priorities of the elements stored
98+
*/
99+
function values(
100+
UintQueue storage queue
101+
) internal view returns (uint256[] memory values_, uint256[] memory priorities_) {
102+
bytes32[] memory vals_ = _values(queue._queue);
103+
104+
assembly {
105+
values_ := vals_
106+
}
107+
108+
priorities_ = _priorities(queue._queue);
109+
}
110+
111+
/**
112+
************************
113+
* Bytes32Queue *
114+
************************
115+
*/
116+
117+
struct Bytes32Queue {
118+
Queue _queue;
119+
}
120+
121+
function add(Bytes32Queue storage queue, bytes32 value_, uint256 priority_) internal {
122+
_add(queue._queue, value_, priority_);
123+
}
124+
125+
function remove(Bytes32Queue storage queue, uint256 index_) internal {
126+
_remove(queue._queue, index_);
127+
}
128+
129+
function removeTop(Bytes32Queue storage queue) internal {
130+
_removeTop(queue._queue);
131+
}
132+
133+
function top(Bytes32Queue storage queue) internal view returns (bytes32) {
134+
return _top(queue._queue);
135+
}
136+
137+
function length(Bytes32Queue storage queue) internal view returns (uint256) {
138+
return _length(queue._queue);
139+
}
140+
141+
function at(
142+
Bytes32Queue storage queue,
143+
uint256 index_
144+
) internal view returns (bytes32, uint256) {
145+
return _at(queue._queue, index_);
146+
}
147+
148+
function values(
149+
Bytes32Queue storage queue
150+
) internal view returns (bytes32[] memory values_, uint256[] memory priorities_) {
151+
values_ = _values(queue._queue);
152+
priorities_ = _priorities(queue._queue);
153+
}
154+
155+
/**
156+
************************
157+
* AddressQueue *
158+
************************
159+
*/
160+
161+
struct AddressQueue {
162+
Queue _queue;
163+
}
164+
165+
function add(AddressQueue storage queue, address value_, uint256 priority_) internal {
166+
_add(queue._queue, bytes32(uint256(uint160(value_))), priority_);
167+
}
168+
169+
function remove(AddressQueue storage queue, uint256 index_) internal {
170+
_remove(queue._queue, index_);
171+
}
172+
173+
function removeTop(AddressQueue storage queue) internal {
174+
_removeTop(queue._queue);
175+
}
176+
177+
function top(AddressQueue storage queue) internal view returns (address) {
178+
return address(uint160(uint256(_top(queue._queue))));
179+
}
180+
181+
function length(AddressQueue storage queue) internal view returns (uint256) {
182+
return _length(queue._queue);
183+
}
184+
185+
function at(
186+
AddressQueue storage queue,
187+
uint256 index_
188+
) internal view returns (address, uint256) {
189+
(bytes32 value_, uint256 priority_) = _at(queue._queue, index_);
190+
191+
return (address(uint160(uint256(value_))), priority_);
192+
}
193+
194+
function values(
195+
AddressQueue storage queue
196+
) internal view returns (address[] memory values_, uint256[] memory priorities_) {
197+
bytes32[] memory vals_ = _values(queue._queue);
198+
199+
assembly {
200+
values_ := vals_
201+
}
202+
203+
priorities_ = _priorities(queue._queue);
204+
}
205+
206+
/**
207+
************************
208+
* Internal Queue *
209+
************************
210+
*/
211+
212+
struct Queue {
213+
bytes32[] _values;
214+
uint256[] _priorities;
215+
}
216+
217+
function _add(Queue storage queue, bytes32 value_, uint256 priority_) private {
218+
queue._values.push(value_);
219+
queue._priorities.push(priority_);
220+
221+
_shiftUp(queue, queue._values.length - 1);
222+
}
223+
224+
function _remove(Queue storage queue, uint256 index_) private {
225+
_requireNotEmpty(queue);
226+
227+
if (index_ > 0) {
228+
queue._priorities[index_] = queue._priorities[0] + 1;
229+
230+
_shiftUp(queue, index_);
231+
}
232+
233+
_removeTop(queue);
234+
}
235+
236+
function _removeTop(Queue storage queue) private {
237+
_requireNotEmpty(queue);
238+
239+
uint256 length_ = _length(queue);
240+
241+
queue._values[0] = queue._values[length_ - 1];
242+
queue._priorities[0] = queue._priorities[length_ - 1];
243+
244+
queue._values.pop();
245+
queue._priorities.pop();
246+
247+
_shiftDown(queue, 0);
248+
}
249+
250+
function _top(Queue storage queue) private view returns (bytes32) {
251+
_requireNotEmpty(queue);
252+
253+
return queue._values[0];
254+
}
255+
256+
function _length(Queue storage queue) private view returns (uint256) {
257+
return queue._values.length;
258+
}
259+
260+
function _at(Queue storage queue, uint256 index_) private view returns (bytes32, uint256) {
261+
return (queue._values[index_], queue._priorities[index_]);
262+
}
263+
264+
function _values(Queue storage queue) private view returns (bytes32[] memory) {
265+
return queue._values;
266+
}
267+
268+
function _priorities(Queue storage queue) private view returns (uint256[] memory) {
269+
return queue._priorities;
270+
}
271+
272+
function _shiftUp(Queue storage queue, uint256 index_) private {
273+
uint256 priority_ = queue._priorities[index_];
274+
275+
while (index_ > 0) {
276+
uint256 parent_ = _parent(index_);
277+
278+
if (queue._priorities[parent_] >= priority_) {
279+
break;
280+
}
281+
282+
_swap(queue, parent_, index_);
283+
284+
index_ = parent_;
285+
}
286+
}
287+
288+
function _shiftDown(Queue storage queue, uint256 index_) private {
289+
while (true) {
290+
uint256 maxIndex_ = _maxPriorityIndex(queue, index_);
291+
292+
if (index_ == maxIndex_) {
293+
break;
294+
}
295+
296+
_swap(queue, maxIndex_, index_);
297+
298+
index_ = maxIndex_;
299+
}
300+
}
301+
302+
function _swap(Queue storage queue, uint256 index1_, uint256 index2_) private {
303+
bytes32[] storage _vals = queue._values;
304+
uint256[] storage _priors = queue._priorities;
305+
306+
(_vals[index1_], _vals[index2_]) = (_vals[index2_], _vals[index1_]);
307+
(_priors[index1_], _priors[index2_]) = (_priors[index2_], _priors[index1_]);
308+
}
309+
310+
function _maxPriorityIndex(
311+
Queue storage queue,
312+
uint256 index_
313+
) private view returns (uint256) {
314+
uint256[] storage _priors = queue._priorities;
315+
316+
uint256 length_ = _priors.length;
317+
uint256 maxIndex_ = index_;
318+
319+
uint256 child_ = _leftChild(index_);
320+
321+
if (child_ < length_ && _priors[child_] > _priors[maxIndex_]) {
322+
maxIndex_ = child_;
323+
}
324+
325+
child_ = _rightChild(index_);
326+
327+
if (child_ < length_ && _priors[child_] > _priors[maxIndex_]) {
328+
maxIndex_ = child_;
329+
}
330+
331+
return maxIndex_;
332+
}
333+
334+
function _parent(uint256 index_) private pure returns (uint256) {
335+
return (index_ - 1) / 2;
336+
}
337+
338+
function _leftChild(uint256 index_) private pure returns (uint256) {
339+
return index_ * 2 + 1;
340+
}
341+
342+
function _rightChild(uint256 index_) private pure returns (uint256) {
343+
return index_ * 2 + 2;
344+
}
345+
346+
function _requireNotEmpty(Queue storage queue) private view {
347+
require(_length(queue) > 0, "PriorityQueue: empty queue");
348+
}
349+
}

0 commit comments

Comments
 (0)