Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
4fec530
implement binary heap
Amxx Jun 16, 2024
8fa2eeb
codespell & lib naming
Amxx Jun 16, 2024
792fcba
tests
Amxx Jun 16, 2024
0c86005
fix fuzzing tests
Amxx Jun 16, 2024
248baf6
codespell
Amxx Jun 16, 2024
53db2ab
update
Amxx Jun 17, 2024
945e0f4
procedural generation
Amxx Jun 17, 2024
df82b15
testing
Amxx Jun 17, 2024
8b965fc
overflow handling
Amxx Jun 21, 2024
e952cf6
add replace and changeset
Amxx Jun 21, 2024
f5fa274
rename top -> peek
Amxx Jun 21, 2024
1f0fef0
internal renaming
Amxx Jun 21, 2024
d0972a3
codespell
Amxx Jun 21, 2024
8e3dda6
regenerate
Amxx Jun 21, 2024
38e1813
auto regenerate
Amxx Jun 21, 2024
02f224d
Update .githooks/pre-push
Amxx Jun 21, 2024
7e88481
up
Amxx Jun 21, 2024
a46cc63
Merge branch 'master' into struct/heap
Amxx Jun 21, 2024
b2fda31
up
Amxx Jun 21, 2024
516f1ca
tests
Amxx Jun 21, 2024
cf1278e
Update test/utils/structs/Heap.test.js
Amxx Jun 21, 2024
5f15d1c
Update test/utils/structs/Heap.test.js
Amxx Jun 21, 2024
32e9b49
Apply suggestions from code review
Amxx Jun 27, 2024
c083d79
regenrate
Amxx Jun 27, 2024
0e6ada0
Merge branch 'master' into struct/heap
Amxx Jul 3, 2024
7c98102
update inline comments
Amxx Jul 15, 2024
a1767d4
update
Amxx Jul 15, 2024
1c1e84b
Address comment for the PR
Amxx Jul 16, 2024
0e7fe7a
rewrite Arrays.sol to use uint256[] as the default, and use Comparato…
Amxx Jul 17, 2024
d495859
Update scripts/generate/templates/Heap.js
Amxx Jul 18, 2024
fe8e902
regenerate
Amxx Jul 18, 2024
3abeb84
Add docs
ernestognw Jul 23, 2024
8801d98
Update scripts/generate/templates/Heap.js
Amxx Jul 23, 2024
f78df0c
Apply suggestions from code review
Amxx Jul 23, 2024
bb37dfb
fix generation + change key type
Amxx Jul 23, 2024
1fb4b81
more invariant check
Amxx Jul 23, 2024
d3308c4
Update scripts/generate/templates/Heap.js
ernestognw Jul 23, 2024
5b07512
Generate
ernestognw Jul 23, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
fix generation + change key type
  • Loading branch information
Amxx committed Jul 23, 2024
commit bb37dfb5e232803ac7a999d45738dfe9ac142bbc
80 changes: 40 additions & 40 deletions contracts/utils/structs/Heap.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

pragma solidity ^0.8.20;

import {Math} from "../math/Math.sol";
import {SafeCast} from "../math/SafeCast.sol";
import {Comparators} from "../Comparators.sol";
import {Panic} from "../Panic.sol";
Expand Down Expand Up @@ -38,6 +39,7 @@ import {Panic} from "../Panic.sol";
* * clear (remove all elements in the set): O(1)
*/
library Heap {
using Math for *;
using SafeCast for *;

/**
Expand All @@ -49,10 +51,13 @@ library Heap {
Uint256HeapNode[] data;
}

/**
* @dev Internal node type for Uint256Heap. Stores a value of type uint256.
*/
struct Uint256HeapNode {
uint256 value;
uint32 index; // position -> value
uint32 lookup; // value -> position
uint64 index; // position -> value
uint64 lookup; // value -> position
}

/**
Expand Down Expand Up @@ -84,22 +89,22 @@ library Heap {
function(uint256, uint256) view returns (bool) comp
) internal returns (uint256) {
unchecked {
uint32 size = length(self);
uint64 size = length(self);
if (size == 0) Panic.panic(Panic.EMPTY_ARRAY_POP);

uint32 last = size - 1;
uint64 last = size - 1;

// get root location (in the data array) and value
Uint256HeapNode storage rootNode = _unsafeNodeAccess(self, 0);
uint32 rootIdx = rootNode.index;
uint64 rootIdx = rootNode.index;
Uint256HeapNode storage rootData = _unsafeNodeAccess(self, rootIdx);
Uint256HeapNode storage lastNode = _unsafeNodeAccess(self, last);
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 (rootIdx != last) {
// get details about the value stored in the last element of the array (that will get pop-ed)
uint32 lastDataIdx = lastNode.lookup;
uint64 lastDataIdx = lastNode.lookup;
uint256 lastDataValue = lastNode.value;
// copy these values to the location of the root (that is safe, and that we no longer use)
rootData.value = lastDataValue;
Expand All @@ -109,7 +114,7 @@ library Heap {
}

// get last leaf location (in the data array) and value
uint32 lastIdx = lastNode.index;
uint64 lastIdx = lastNode.index;
uint256 lastValue = _unsafeNodeAccess(self, lastIdx).value;

// move the last leaf to the root, pop last leaf ...
Expand Down Expand Up @@ -146,8 +151,8 @@ library Heap {
uint256 value,
function(uint256, uint256) view returns (bool) comp
) internal {
uint32 size = length(self);
if (size == type(uint32).max) Panic.panic(Panic.RESOURCE_ERROR);
uint64 size = length(self);
if (size == type(uint64).max) Panic.panic(Panic.RESOURCE_ERROR);

self.data.push(Uint256HeapNode({index: size, lookup: size, value: value}));
_siftUp(self, size, value, comp);
Expand Down Expand Up @@ -176,11 +181,11 @@ library Heap {
uint256 newValue,
function(uint256, uint256) view returns (bool) comp
) internal returns (uint256) {
uint32 size = length(self);
uint64 size = length(self);
if (size == 0) Panic.panic(Panic.EMPTY_ARRAY_POP);

// position of the node that holds the data for the root
uint32 rootIdx = _unsafeNodeAccess(self, 0).index;
uint64 rootIdx = _unsafeNodeAccess(self, 0).index;
// storage pointer to the node that holds the data for the root
Uint256HeapNode storage rootData = _unsafeNodeAccess(self, rootIdx);

Expand All @@ -198,8 +203,8 @@ library Heap {
/**
* @dev Returns the number of elements in the heap.
*/
function length(Uint256Heap storage self) internal view returns (uint32) {
return self.data.length.toUint32();
function length(Uint256Heap storage self) internal view returns (uint64) {
return self.data.length.toUint64();
}

/**
Expand All @@ -216,11 +221,11 @@ library Heap {
/*
* @dev Swap node `i` and `j` in the tree.
*/
function _swap(Uint256Heap storage self, uint32 i, uint32 j) private {
function _swap(Uint256Heap storage self, uint64 i, uint64 j) private {
Uint256HeapNode storage ni = _unsafeNodeAccess(self, i);
Uint256HeapNode storage nj = _unsafeNodeAccess(self, j);
uint32 ii = ni.index;
uint32 jj = nj.index;
uint64 ii = ni.index;
uint64 jj = nj.index;
// update pointers to the data (swap the value)
ni.index = jj;
nj.index = ii;
Expand All @@ -239,32 +244,28 @@ library Heap {
*/
function _siftDown(
Uint256Heap storage self,
uint32 size,
uint32 pos,
uint64 size,
uint64 pos,
uint256 value,
function(uint256, uint256) view returns (bool) comp
) private {
uint256 left = 2 * pos + 1; // this could overflow uint32
uint256 right = 2 * pos + 2; // this could overflow uint32
uint256 left = 2 * pos + 1; // this could overflow uint64
uint256 right = 2 * pos + 2; // this could overflow uint64

if (right < size) {
// the check guarantees that `left` and `right` are both valid uint32
uint32 lIndex = uint32(left);
uint32 rIndex = uint32(right);
uint64 lIndex = uint64(left);
uint64 rIndex = uint64(right);
uint256 lValue = _unsafeNodeAccess(self, _unsafeNodeAccess(self, lIndex).index).value;
uint256 rValue = _unsafeNodeAccess(self, _unsafeNodeAccess(self, rIndex).index).value;
if (comp(lValue, value) || comp(rValue, value)) {
if (comp(lValue, rValue)) {
_swap(self, pos, lIndex);
_siftDown(self, size, lIndex, value, comp);
} else {
_swap(self, pos, rIndex);
_siftDown(self, size, rIndex, value, comp);
}
uint64 index = uint64(comp(lValue, rValue).ternary(lIndex, rIndex));
_swap(self, pos, index);
_siftDown(self, size, index, value, comp);
}
} else if (left < size) {
// the check guarantees that `left` is a valid uint32
uint32 lIndex = uint32(left);
uint64 lIndex = uint64(left);
uint256 lValue = _unsafeNodeAccess(self, _unsafeNodeAccess(self, lIndex).index).value;
if (comp(lValue, value)) {
_swap(self, pos, lIndex);
Expand All @@ -283,13 +284,13 @@ library Heap {
*/
function _siftUp(
Uint256Heap storage self,
uint32 pos,
uint64 pos,
uint256 value,
function(uint256, uint256) view returns (bool) comp
) private {
unchecked {
while (pos > 0) {
uint32 parent = (pos - 1) / 2;
uint64 parent = (pos - 1) / 2;
uint256 parentValue = _unsafeNodeAccess(self, _unsafeNodeAccess(self, parent).index).value;
if (comp(parentValue, value)) break;
_swap(self, pos, parent);
Expand All @@ -300,7 +301,7 @@ library Heap {

function _unsafeNodeAccess(
Uint256Heap storage self,
uint32 pos
uint64 pos
) private pure returns (Uint256HeapNode storage result) {
assembly ("memory-safe") {
mstore(0x00, self.slot)
Expand All @@ -317,6 +318,9 @@ library Heap {
Uint208HeapNode[] data;
}

/**
* @dev Internal node type for Uint208Heap. Stores a value of type uint208.
*/
struct Uint208HeapNode {
uint208 value;
uint24 index; // position -> value
Expand Down Expand Up @@ -522,13 +526,9 @@ library Heap {
uint208 lValue = _unsafeNodeAccess(self, _unsafeNodeAccess(self, lIndex).index).value;
uint208 rValue = _unsafeNodeAccess(self, _unsafeNodeAccess(self, rIndex).index).value;
if (comp(lValue, value) || comp(rValue, value)) {
if (comp(lValue, rValue)) {
_swap(self, pos, lIndex);
_siftDown(self, size, lIndex, value, comp);
} else {
_swap(self, pos, rIndex);
_siftDown(self, size, rIndex, value, comp);
}
uint24 index = uint24(comp(lValue, rValue).ternary(lIndex, rIndex));
_swap(self, pos, index);
_siftDown(self, size, index, value, comp);
}
} else if (left < size) {
// the check guarantees that `left` is a valid uint32
Expand Down
4 changes: 3 additions & 1 deletion scripts/generate/templates/Heap.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const { capitalize } = require('../../helpers');
const header = `\
pragma solidity ^0.8.20;

import {Math} from "../math/Math.sol";
import {SafeCast} from "../math/SafeCast.sol";
import {Comparators} from "../Comparators.sol";
import {Panic} from "../Panic.sol";
Expand Down Expand Up @@ -260,7 +261,7 @@ function _siftDown(
${valueType} lValue = _unsafeNodeAccess(self, _unsafeNodeAccess(self, lIndex).index).value;
${valueType} rValue = _unsafeNodeAccess(self, _unsafeNodeAccess(self, rIndex).index).value;
if (comp(lValue, value) || comp(rValue, value)) {
index = ternary(comp(lValue, rValue), lIndex, rIndex);
${indexType} index = ${indexType}(comp(lValue, rValue).ternary(lIndex, rIndex));
_swap(self, pos, index);
_siftDown(self, size, index, value, comp);
}
Expand Down Expand Up @@ -317,6 +318,7 @@ module.exports = format(
'library Heap {',
format(
[].concat(
'using Math for *;',
'using SafeCast for *;',
'',
TYPES.map(type => generate(type)),
Expand Down
2 changes: 1 addition & 1 deletion scripts/generate/templates/Heap.opts.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ const makeType = (valueSize, indexSize) => ({
});

module.exports = {
TYPES: [makeType(256, 32), makeType(208, 24)],
TYPES: [makeType(256, 64), makeType(208, 24)],
};