|
|
@ -26,17 +26,17 @@ import {Panic} from "../Panic.sol"; |
|
|
|
* ``` |
|
|
|
* ``` |
|
|
|
* |
|
|
|
* |
|
|
|
* The structure is ordered so that each node is bigger than its parent. An immediate consequence is that the |
|
|
|
* The structure is ordered so that each node is bigger than its parent. An immediate consequence is that the |
|
|
|
* highest priority value is the one at the root. This value can be lookup up in constant time (O(1)) at |
|
|
|
* highest priority value is the one at the root. This value can be looked up in constant time (O(1)) at |
|
|
|
* `heap.data[heap.data[0].index].value` |
|
|
|
* `heap.data[heap.data[0].index].value` |
|
|
|
* |
|
|
|
* |
|
|
|
* The structure is designed to perform the following operations with the corresponding complexities: |
|
|
|
* The structure is designed to perform the following operations with the corresponding complexities: |
|
|
|
* |
|
|
|
* |
|
|
|
* * peek (get the highest priority in set): O(1) |
|
|
|
* * peek (get the highest priority value): O(1) |
|
|
|
* * insert (insert a value in the set): 0(log(n)) |
|
|
|
* * insert (insert a value): O(log(n)) |
|
|
|
* * pop (remove the highest priority value in set): O(log(n)) |
|
|
|
* * pop (remove the highest priority value): O(log(n)) |
|
|
|
* * replace (replace the highest priority value in set with a new value): O(log(n)) |
|
|
|
* * replace (replace the highest priority value with a new value): O(log(n)) |
|
|
|
* * length (get the number of elements in the set): O(1) |
|
|
|
* * length (get the number of elements): O(1) |
|
|
|
* * clear (remove all elements in the set): O(1) |
|
|
|
* * clear (remove all elements): O(1) |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
library Heap { |
|
|
|
library Heap { |
|
|
|
using Math for *; |
|
|
|
using Math for *; |
|
|
@ -45,7 +45,7 @@ library Heap { |
|
|
|
/** |
|
|
|
/** |
|
|
|
* @dev Binary heap that support values of type uint256. |
|
|
|
* @dev Binary heap that support values of type uint256. |
|
|
|
* |
|
|
|
* |
|
|
|
* Each element of that structures uses 2 storage slots. |
|
|
|
* Each element of that structure uses 2 storage slots. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
struct Uint256Heap { |
|
|
|
struct Uint256Heap { |
|
|
|
Uint256HeapNode[] data; |
|
|
|
Uint256HeapNode[] data; |
|
|
@ -101,9 +101,9 @@ library Heap { |
|
|
|
Uint256HeapNode storage lastNode = _unsafeNodeAccess(self, last); |
|
|
|
Uint256HeapNode storage lastNode = _unsafeNodeAccess(self, last); |
|
|
|
uint256 rootDataValue = rootData.value; |
|
|
|
uint256 rootDataValue = rootData.value; |
|
|
|
|
|
|
|
|
|
|
|
// if root is not the last element of the data array (that will get pop-ed), reorder the data array. |
|
|
|
// if root is not the last element of the data array (that will get popped), reorder the data array. |
|
|
|
if (rootIdx != last) { |
|
|
|
if (rootIdx != last) { |
|
|
|
// get details about the value stored in the last element of the array (that will get pop-ed) |
|
|
|
// get details about the value stored in the last element of the array (that will get popped) |
|
|
|
uint64 lastDataIdx = lastNode.lookup; |
|
|
|
uint64 lastDataIdx = lastNode.lookup; |
|
|
|
uint256 lastDataValue = lastNode.value; |
|
|
|
uint256 lastDataValue = lastNode.value; |
|
|
|
// copy these values to the location of the root (that is safe, and that we no longer use) |
|
|
|
// copy these values to the location of the root (that is safe, and that we no longer use) |
|
|
@ -253,7 +253,7 @@ library Heap { |
|
|
|
uint256 right = 2 * pos + 2; // this could overflow uint64 |
|
|
|
uint256 right = 2 * pos + 2; // this could overflow uint64 |
|
|
|
|
|
|
|
|
|
|
|
if (right < size) { |
|
|
|
if (right < size) { |
|
|
|
// the check guarantees that `left` and `right` are both valid uint32 |
|
|
|
// the check guarantees that `left` and `right` are both valid uint64 |
|
|
|
uint64 lIndex = uint64(left); |
|
|
|
uint64 lIndex = uint64(left); |
|
|
|
uint64 rIndex = uint64(right); |
|
|
|
uint64 rIndex = uint64(right); |
|
|
|
uint256 lValue = _unsafeNodeAccess(self, _unsafeNodeAccess(self, lIndex).index).value; |
|
|
|
uint256 lValue = _unsafeNodeAccess(self, _unsafeNodeAccess(self, lIndex).index).value; |
|
|
@ -264,7 +264,7 @@ library Heap { |
|
|
|
_siftDown(self, size, index, value, comp); |
|
|
|
_siftDown(self, size, index, value, comp); |
|
|
|
} |
|
|
|
} |
|
|
|
} else if (left < size) { |
|
|
|
} else if (left < size) { |
|
|
|
// the check guarantees that `left` is a valid uint32 |
|
|
|
// the check guarantees that `left` is a valid uint64 |
|
|
|
uint64 lIndex = uint64(left); |
|
|
|
uint64 lIndex = uint64(left); |
|
|
|
uint256 lValue = _unsafeNodeAccess(self, _unsafeNodeAccess(self, lIndex).index).value; |
|
|
|
uint256 lValue = _unsafeNodeAccess(self, _unsafeNodeAccess(self, lIndex).index).value; |
|
|
|
if (comp(lValue, value)) { |
|
|
|
if (comp(lValue, value)) { |
|
|
@ -279,7 +279,7 @@ library Heap { |
|
|
|
* comparator, and moving toward the root of the underlying tree. |
|
|
|
* comparator, and moving toward the root of the underlying tree. |
|
|
|
* |
|
|
|
* |
|
|
|
* NOTE: This is a private function that is called in a trusted context with already cached parameters. `value` |
|
|
|
* NOTE: This is a private function that is called in a trusted context with already cached parameters. `value` |
|
|
|
* could be extracted from `self` and `pos`, but that would require redundant storage read. This parameters is not |
|
|
|
* could be extracted from `self` and `pos`, but that would require redundant storage read. These parameters are not |
|
|
|
* verified. It is the caller role to make sure the parameters are correct. |
|
|
|
* verified. It is the caller role to make sure the parameters are correct. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
function _siftUp( |
|
|
|
function _siftUp( |
|
|
@ -312,7 +312,7 @@ library Heap { |
|
|
|
/** |
|
|
|
/** |
|
|
|
* @dev Binary heap that support values of type uint208. |
|
|
|
* @dev Binary heap that support values of type uint208. |
|
|
|
* |
|
|
|
* |
|
|
|
* Each element of that structures uses 1 storage slots. |
|
|
|
* Each element of that structure uses 1 storage slots. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
struct Uint208Heap { |
|
|
|
struct Uint208Heap { |
|
|
|
Uint208HeapNode[] data; |
|
|
|
Uint208HeapNode[] data; |
|
|
@ -368,9 +368,9 @@ library Heap { |
|
|
|
Uint208HeapNode storage lastNode = _unsafeNodeAccess(self, last); |
|
|
|
Uint208HeapNode storage lastNode = _unsafeNodeAccess(self, last); |
|
|
|
uint208 rootDataValue = rootData.value; |
|
|
|
uint208 rootDataValue = rootData.value; |
|
|
|
|
|
|
|
|
|
|
|
// if root is not the last element of the data array (that will get pop-ed), reorder the data array. |
|
|
|
// if root is not the last element of the data array (that will get popped), reorder the data array. |
|
|
|
if (rootIdx != last) { |
|
|
|
if (rootIdx != last) { |
|
|
|
// get details about the value stored in the last element of the array (that will get pop-ed) |
|
|
|
// get details about the value stored in the last element of the array (that will get popped) |
|
|
|
uint24 lastDataIdx = lastNode.lookup; |
|
|
|
uint24 lastDataIdx = lastNode.lookup; |
|
|
|
uint208 lastDataValue = lastNode.value; |
|
|
|
uint208 lastDataValue = lastNode.value; |
|
|
|
// copy these values to the location of the root (that is safe, and that we no longer use) |
|
|
|
// copy these values to the location of the root (that is safe, and that we no longer use) |
|
|
@ -520,7 +520,7 @@ library Heap { |
|
|
|
uint256 right = 2 * pos + 2; // this could overflow uint24 |
|
|
|
uint256 right = 2 * pos + 2; // this could overflow uint24 |
|
|
|
|
|
|
|
|
|
|
|
if (right < size) { |
|
|
|
if (right < size) { |
|
|
|
// the check guarantees that `left` and `right` are both valid uint32 |
|
|
|
// the check guarantees that `left` and `right` are both valid uint24 |
|
|
|
uint24 lIndex = uint24(left); |
|
|
|
uint24 lIndex = uint24(left); |
|
|
|
uint24 rIndex = uint24(right); |
|
|
|
uint24 rIndex = uint24(right); |
|
|
|
uint208 lValue = _unsafeNodeAccess(self, _unsafeNodeAccess(self, lIndex).index).value; |
|
|
|
uint208 lValue = _unsafeNodeAccess(self, _unsafeNodeAccess(self, lIndex).index).value; |
|
|
@ -531,7 +531,7 @@ library Heap { |
|
|
|
_siftDown(self, size, index, value, comp); |
|
|
|
_siftDown(self, size, index, value, comp); |
|
|
|
} |
|
|
|
} |
|
|
|
} else if (left < size) { |
|
|
|
} else if (left < size) { |
|
|
|
// the check guarantees that `left` is a valid uint32 |
|
|
|
// the check guarantees that `left` is a valid uint24 |
|
|
|
uint24 lIndex = uint24(left); |
|
|
|
uint24 lIndex = uint24(left); |
|
|
|
uint208 lValue = _unsafeNodeAccess(self, _unsafeNodeAccess(self, lIndex).index).value; |
|
|
|
uint208 lValue = _unsafeNodeAccess(self, _unsafeNodeAccess(self, lIndex).index).value; |
|
|
|
if (comp(lValue, value)) { |
|
|
|
if (comp(lValue, value)) { |
|
|
@ -546,7 +546,7 @@ library Heap { |
|
|
|
* comparator, and moving toward the root of the underlying tree. |
|
|
|
* comparator, and moving toward the root of the underlying tree. |
|
|
|
* |
|
|
|
* |
|
|
|
* NOTE: This is a private function that is called in a trusted context with already cached parameters. `value` |
|
|
|
* NOTE: This is a private function that is called in a trusted context with already cached parameters. `value` |
|
|
|
* could be extracted from `self` and `pos`, but that would require redundant storage read. This parameters is not |
|
|
|
* could be extracted from `self` and `pos`, but that would require redundant storage read. These parameters are not |
|
|
|
* verified. It is the caller role to make sure the parameters are correct. |
|
|
|
* verified. It is the caller role to make sure the parameters are correct. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
function _siftUp( |
|
|
|
function _siftUp( |
|
|
|