From bad028e75c7af44d52a95bcde355b7d6a916e645 Mon Sep 17 00:00:00 2001 From: mllwchrry Date: Mon, 20 May 2024 20:56:42 +0300 Subject: [PATCH 01/11] AVL tree library implementation --- contracts/libs/data-structures/AvlTree.sol | 542 +++++++++++++++++++++ 1 file changed, 542 insertions(+) create mode 100644 contracts/libs/data-structures/AvlTree.sol diff --git a/contracts/libs/data-structures/AvlTree.sol b/contracts/libs/data-structures/AvlTree.sol new file mode 100644 index 00000000..4c9142fd --- /dev/null +++ b/contracts/libs/data-structures/AvlTree.sol @@ -0,0 +1,542 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.4; + +import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; + +/** + * @notice The implementation of AVL tree. + */ +library AvlTree { + /** + ********************* + * UintAVL * + ********************* + */ + + struct UintAVL { + Tree _tree; + } + + function setComparator( + UintAVL storage tree, + function(bytes32, bytes32) pure returns (int8) comparator_ + ) internal { + _setComparator(tree._tree, comparator_); + } + + function insert(UintAVL storage tree, uint256 value_) internal { + _insert(tree._tree, bytes32(value_)); + } + + function remove(UintAVL storage tree, uint256 value_) internal { + _remove(tree._tree, bytes32(value_)); + } + + function search(UintAVL storage tree, uint256 value_) internal view returns (bool) { + return _search(tree._tree, bytes32(value_)); + } + + function getMin(UintAVL storage tree) internal view returns (uint256) { + return uint256(_getMin(tree._tree.tree, tree._tree.root)); + } + + function getMax(UintAVL storage tree) internal view returns (uint256) { + return uint256(_getMax(tree._tree.tree, tree._tree.root)); + } + + function root(UintAVL storage tree) internal view returns (uint256) { + return uint256(tree._tree.root); + } + + function treeSize(UintAVL storage tree) internal view returns (uint256) { + return tree._tree.treeSize; + } + + function inOrderTraversal(UintAVL storage tree) internal view returns (uint256[] memory) { + bytes32[] memory bytesTraversal_ = _getTraversal(tree._tree, _inOrderTraversal); + uint256[] memory uintTraversal_ = new uint256[](bytesTraversal_.length); + + for (uint256 i = 0; i < bytesTraversal_.length; i++) { + uintTraversal_[i] = uint256(bytesTraversal_[i]); + } + + return uintTraversal_; + } + + function preOrderTraversal(UintAVL storage tree) internal view returns (uint256[] memory) { + bytes32[] memory bytesTraversal_ = _getTraversal(tree._tree, _preOrderTraversal); + uint256[] memory uintTraversal_ = new uint256[](bytesTraversal_.length); + + for (uint256 i = 0; i < bytesTraversal_.length; i++) { + uintTraversal_[i] = uint256(bytesTraversal_[i]); + } + + return uintTraversal_; + } + + function postOrderTraversal(UintAVL storage tree) internal view returns (uint256[] memory) { + bytes32[] memory bytesTraversal_ = _getTraversal(tree._tree, _postOrderTraversal); + uint256[] memory uintTraversal_ = new uint256[](bytesTraversal_.length); + + for (uint256 i = 0; i < bytesTraversal_.length; i++) { + uintTraversal_[i] = uint256(bytesTraversal_[i]); + } + + return uintTraversal_; + } + + function isCustomComparatorSet(UintAVL storage tree) internal view returns (bool) { + return tree._tree.isCustomComparatorSet; + } + + /** + ********************** + * Bytes32AVL * + ********************** + */ + + struct Bytes32AVL { + Tree _tree; + } + + function setComparator( + Bytes32AVL storage tree, + function(bytes32, bytes32) pure returns (int8) comparator_ + ) internal { + _setComparator(tree._tree, comparator_); + } + + function insert(Bytes32AVL storage tree, bytes32 value_) internal { + _insert(tree._tree, value_); + } + + function remove(Bytes32AVL storage tree, bytes32 value_) internal { + _remove(tree._tree, value_); + } + + function search(Bytes32AVL storage tree, bytes32 value_) internal view returns (bool) { + return _search(tree._tree, value_); + } + + function getMin(Bytes32AVL storage tree) internal view returns (bytes32) { + return _getMin(tree._tree.tree, tree._tree.root); + } + + function getMax(Bytes32AVL storage tree) internal view returns (bytes32) { + return _getMax(tree._tree.tree, tree._tree.root); + } + + function root(Bytes32AVL storage tree) internal view returns (bytes32) { + return tree._tree.root; + } + + function treeSize(Bytes32AVL storage tree) internal view returns (uint256) { + return tree._tree.treeSize; + } + + function inOrderTraversal(Bytes32AVL storage tree) internal view returns (bytes32[] memory) { + return _getTraversal(tree._tree, _inOrderTraversal); + } + + function preOrderTraversal(Bytes32AVL storage tree) internal view returns (bytes32[] memory) { + return _getTraversal(tree._tree, _preOrderTraversal); + } + + function postOrderTraversal(Bytes32AVL storage tree) internal view returns (bytes32[] memory) { + return _getTraversal(tree._tree, _postOrderTraversal); + } + + function isCustomComparatorSet(Bytes32AVL storage tree) internal view returns (bool) { + return tree._tree.isCustomComparatorSet; + } + + /** + ********************** + * AddressAVL * + ********************** + */ + + struct AddressAVL { + Tree _tree; + } + + function setComparator( + AddressAVL storage tree, + function(bytes32, bytes32) pure returns (int8) comparator_ + ) internal { + _setComparator(tree._tree, comparator_); + } + + function insert(AddressAVL storage tree, address value_) internal { + _insert(tree._tree, bytes32(uint256(uint160(value_)))); + } + + function remove(AddressAVL storage tree, address value_) internal { + _remove(tree._tree, bytes32(uint256(uint160(value_)))); + } + + function search(AddressAVL storage tree, address value_) private view returns (bool) { + return _search(tree._tree, bytes32(uint256(uint160(value_)))); + } + + function getMin(AddressAVL storage tree) private view returns (address) { + return address(uint160(uint256(_getMin(tree._tree.tree, tree._tree.root)))); + } + + function getMax(AddressAVL storage tree) private view returns (address) { + return address(uint160(uint256(_getMax(tree._tree.tree, tree._tree.root)))); + } + + function root(AddressAVL storage tree) internal view returns (address) { + return address(uint160(uint256(tree._tree.root))); + } + + function treeSize(AddressAVL storage tree) internal view returns (uint256) { + return tree._tree.treeSize; + } + + function inOrderTraversal(AddressAVL storage tree) internal view returns (address[] memory) { + bytes32[] memory bytesTraversal_ = _getTraversal(tree._tree, _inOrderTraversal); + address[] memory addressTraversal_ = new address[](bytesTraversal_.length); + + for (uint256 i = 0; i < bytesTraversal_.length; i++) { + addressTraversal_[i] = address(uint160(uint256(bytesTraversal_[i]))); + } + + return addressTraversal_; + } + + function preOrderTraversal(AddressAVL storage tree) internal view returns (address[] memory) { + bytes32[] memory bytesTraversal_ = _getTraversal(tree._tree, _preOrderTraversal); + address[] memory addressTraversal_ = new address[](bytesTraversal_.length); + + for (uint256 i = 0; i < bytesTraversal_.length; i++) { + addressTraversal_[i] = address(uint160(uint256(bytesTraversal_[i]))); + } + + return addressTraversal_; + } + + function postOrderTraversal(AddressAVL storage tree) internal view returns (address[] memory) { + bytes32[] memory bytesTraversal_ = _getTraversal(tree._tree, _postOrderTraversal); + address[] memory addressTraversal_ = new address[](bytesTraversal_.length); + + for (uint256 i = 0; i < bytesTraversal_.length; i++) { + addressTraversal_[i] = address(uint160(uint256(bytesTraversal_[i]))); + } + + return addressTraversal_; + } + + function isCustomComparatorSet(AddressAVL storage tree) internal view returns (bool) { + return tree._tree.isCustomComparatorSet; + } + + /** + ************************* + * Internal Tree * + ************************* + */ + + struct Node { + bytes32 key; + uint256 height; + bytes32 left; + bytes32 right; + } + + struct Tree { + mapping(bytes32 => Node) tree; + bytes32 root; + uint256 treeSize; + bool isCustomComparatorSet; + function(bytes32, bytes32) pure returns (int8) comparator; + } + + function _setComparator( + Tree storage tree, + function(bytes32, bytes32) pure returns (int8) comparator_ + ) private { + require(tree.treeSize == 0, "AvlTree: the tree must be empty"); + + tree.isCustomComparatorSet = true; + + tree.comparator = comparator_; + } + + function _insert(Tree storage tree, bytes32 key_) private { + require(key_ != 0, "AvlTree: key is not allowed to be 0"); + require(tree.tree[key_].key != key_, "AvlSegmentTree: the node already exists"); + + function(bytes32, bytes32) pure returns (int8) comparator_ = tree.isCustomComparatorSet + ? tree.comparator + : _defaultComparator; + + tree.root = _insertNode(tree.tree, tree.root, key_, comparator_); + + tree.treeSize++; + } + + function _remove(Tree storage tree, bytes32 key_) private { + require(key_ != 0, "AvlTree: key is not allowed to be 0"); + require(tree.treeSize != 0, "AvlSegmentTree: tree is empty"); + + require(tree.tree[key_].key == key_, "AvlSegmentTree: the node doesn't exist"); + + function(bytes32, bytes32) pure returns (int8) comparator_ = tree.isCustomComparatorSet + ? tree.comparator + : _defaultComparator; + + tree.root = _removeNode(tree.tree, tree.root, key_, comparator_); + + tree.treeSize--; + } + + function _insertNode( + mapping(bytes32 => Node) storage _tree, + bytes32 node_, + bytes32 key_, + function(bytes32, bytes32) pure returns (int8) comparator_ + ) private returns (bytes32) { + if (node_ == 0) { + _tree[key_] = Node({key: key_, left: 0, right: 0, height: 1}); + + return key_; + } + + if (comparator_(key_, node_) <= 0) { + _tree[node_].left = _insertNode(_tree, _tree[node_].left, key_, comparator_); + } else { + _tree[node_].right = _insertNode(_tree, _tree[node_].right, key_, comparator_); + } + + return _balance(_tree, node_); + } + + function _removeNode( + mapping(bytes32 => Node) storage _tree, + bytes32 node_, + bytes32 key_, + function(bytes32, bytes32) pure returns (int8) comparator_ + ) private returns (bytes32) { + if (comparator_(key_, node_) == 0) { + bytes32 left_ = _tree[node_].left; + bytes32 right_ = _tree[node_].right; + + _tree[node_] = _tree[0]; + + if (right_ == 0) { + return left_; + } + + bytes32 temp_; + + for (temp_ = right_; _tree[temp_].left != 0; temp_ = _tree[temp_].left) {} + + _tree[temp_].right = _removeMin(_tree, right_); + _tree[temp_].left = left_; + + return _balance(_tree, temp_); + } else if (comparator_(key_, node_) < 0) { + _tree[node_].left = _removeNode(_tree, _tree[node_].left, key_, comparator_); + } else { + _tree[node_].right = _removeNode(_tree, _tree[node_].right, key_, comparator_); + } + + return _balance(_tree, node_); + } + + function _removeMin( + mapping(bytes32 => Node) storage _tree, + bytes32 node_ + ) private returns (bytes32) { + Node storage _node = _tree[node_]; + + if (_node.left == 0) { + return _node.right; + } + + _node.left = _removeMin(_tree, _node.left); + + return _balance(_tree, node_); + } + + function _rotateLeft( + mapping(bytes32 => Node) storage _tree, + bytes32 node_ + ) private returns (bytes32) { + Node storage _node = _tree[node_]; + + bytes32 temp_ = _node.left; + _node.left = _tree[temp_].right; + _tree[temp_].right = node_; + + _updateHeight(_tree, node_); + _updateHeight(_tree, temp_); + + return temp_; + } + + function _rotateRight( + mapping(bytes32 => Node) storage _tree, + bytes32 node_ + ) private returns (bytes32) { + Node storage _node = _tree[node_]; + + bytes32 temp_ = _node.right; + _node.right = _tree[temp_].left; + _tree[temp_].left = node_; + + _updateHeight(_tree, node_); + _updateHeight(_tree, temp_); + + return temp_; + } + + function _balance( + mapping(bytes32 => Node) storage _tree, + bytes32 node_ + ) private returns (bytes32) { + _updateHeight(_tree, node_); + + Node storage _left = _tree[_tree[node_].left]; + Node storage _right = _tree[_tree[node_].right]; + + if (_left.height > _right.height + 1) { + if (_tree[_left.right].height > _tree[_left.left].height) { + _tree[node_].left = _rotateRight(_tree, _tree[node_].left); + } + + return _rotateLeft(_tree, node_); + } else if (_right.height > _left.height + 1) { + if (_tree[_right.left].height > _tree[_right.right].height) { + _tree[node_].right = _rotateLeft(_tree, _tree[node_].right); + } + + return _rotateRight(_tree, node_); + } + + return node_; + } + + function _updateHeight(mapping(bytes32 => Node) storage _tree, bytes32 node_) private { + Node storage _node = _tree[node_]; + + _node.height = 1 + Math.max(_tree[_node.left].height, _tree[_node.right].height); + } + + function _search(Tree storage tree, bytes32 key_) private view returns (bool) { + require(key_ != 0, "AvlTree: key is not allowed to be 0"); + require(tree.treeSize != 0, "AvlTree: tree is empty"); + + return tree.tree[key_].key == key_; + } + + function _getMin( + mapping(bytes32 => Node) storage _tree, + bytes32 node_ + ) private view returns (bytes32) { + require(node_ != 0, "AvlTree: tree is empty"); + + while (_tree[node_].left != 0) { + node_ = _tree[node_].left; + } + + return node_; + } + + function _getMax( + mapping(bytes32 => Node) storage _tree, + bytes32 node_ + ) private view returns (bytes32) { + require(node_ != 0, "AvlTree: tree is empty"); + + while (_tree[node_].right != 0) { + node_ = _tree[node_].right; + } + + return node_; + } + + function _getTraversal( + Tree storage tree, + function(mapping(bytes32 => Node) storage, bytes32, bytes32[] memory, uint256) + view + returns (uint256) traversalFunction_ + ) private view returns (bytes32[] memory) { + require(tree.treeSize != 0, "AvlSegmentTreeMock: Tree is empty"); + + bytes32[] memory keys_ = new bytes32[](tree.treeSize); + + traversalFunction_(tree.tree, tree.root, keys_, 0); + + return keys_; + } + + function _inOrderTraversal( + mapping(bytes32 => Node) storage _tree, + bytes32 node_, + bytes32[] memory keys_, + uint256 index_ + ) private view returns (uint256) { + if (node_ == 0) { + return index_; + } + + index_ = _inOrderTraversal(_tree, _tree[node_].left, keys_, index_); + + if (_tree[node_].key != 0) { + keys_[index_++] = _tree[node_].key; + } + + index_ = _inOrderTraversal(_tree, _tree[node_].right, keys_, index_); + + return index_; + } + + function _preOrderTraversal( + mapping(bytes32 => Node) storage _tree, + bytes32 node_, + bytes32[] memory keys_, + uint256 index_ + ) private view returns (uint256) { + if (node_ == 0) { + return index_; + } + + if (_tree[node_].key != 0) { + keys_[index_++] = _tree[node_].key; + } + + index_ = _preOrderTraversal(_tree, _tree[node_].left, keys_, index_); + index_ = _preOrderTraversal(_tree, _tree[node_].right, keys_, index_); + + return index_; + } + + function _postOrderTraversal( + mapping(bytes32 => Node) storage _tree, + bytes32 node_, + bytes32[] memory keys_, + uint256 index_ + ) private view returns (uint256) { + if (node_ == 0) { + return index_; + } + + index_ = _postOrderTraversal(_tree, _tree[node_].left, keys_, index_); + index_ = _postOrderTraversal(_tree, _tree[node_].right, keys_, index_); + + if (_tree[node_].key != 0) { + keys_[index_++] = _tree[node_].key; + } + + return index_; + } + + function _defaultComparator(bytes32 a, bytes32 b) private pure returns (int8) { + if (a < b) return -1; + if (a > b) return 1; + return 0; + } +} From 1fc470c819d370c8c822395996ddab206949748e Mon Sep 17 00:00:00 2001 From: mllwchrry Date: Wed, 22 May 2024 10:59:27 +0300 Subject: [PATCH 02/11] added value and range query --- contracts/libs/data-structures/AvlTree.sol | 281 +++++++++++++++++---- 1 file changed, 225 insertions(+), 56 deletions(-) diff --git a/contracts/libs/data-structures/AvlTree.sol b/contracts/libs/data-structures/AvlTree.sol index 4c9142fd..b286388a 100644 --- a/contracts/libs/data-structures/AvlTree.sol +++ b/contracts/libs/data-structures/AvlTree.sol @@ -19,21 +19,53 @@ library AvlTree { function setComparator( UintAVL storage tree, - function(bytes32, bytes32) pure returns (int8) comparator_ + function(mapping(bytes32 => Node) storage, bytes32, bytes32) + view + returns (int8) comparator_ ) internal { _setComparator(tree._tree, comparator_); } - function insert(UintAVL storage tree, uint256 value_) internal { - _insert(tree._tree, bytes32(value_)); + function insert(UintAVL storage tree, uint256 key_, bytes calldata value_) internal { + _insert(tree._tree, bytes32(key_), value_); + } + + function remove(UintAVL storage tree, uint256 key_) internal { + _remove(tree._tree, bytes32(key_)); } - function remove(UintAVL storage tree, uint256 value_) internal { - _remove(tree._tree, bytes32(value_)); + function search(UintAVL storage tree, uint256 key_) internal view returns (bool) { + return _search(tree._tree, bytes32(key_)); } - function search(UintAVL storage tree, uint256 value_) internal view returns (bool) { - return _search(tree._tree, bytes32(value_)); + function getValue(UintAVL storage tree, uint256 key_) internal view returns (bytes storage) { + return tree._tree.tree[bytes32(key_)].value; + } + + function rangeQuery( + UintAVL storage tree, + uint256 min_, + uint256 max_ + ) internal view returns (uint256[] memory) { + require(min_ <= max_, "AvlTree: min should be less than or equal to max"); + + bytes32[] memory bytesResult_ = new bytes32[](tree._tree.treeSize); + uint256 count_ = _rangeQuery( + tree._tree.tree, + tree._tree.root, + bytes32(min_), + bytes32(max_), + bytesResult_, + 0, + _getComparator(tree._tree) + ); + + uint256[] memory uintResult_ = new uint256[](count_); + for (uint256 i = 0; i < count_; i++) { + uintResult_[i] = uint256(bytesResult_[i]); + } + + return uintResult_; } function getMin(UintAVL storage tree) internal view returns (uint256) { @@ -101,21 +133,56 @@ library AvlTree { function setComparator( Bytes32AVL storage tree, - function(bytes32, bytes32) pure returns (int8) comparator_ + function(mapping(bytes32 => Node) storage, bytes32, bytes32) + view + returns (int8) comparator_ ) internal { _setComparator(tree._tree, comparator_); } - function insert(Bytes32AVL storage tree, bytes32 value_) internal { - _insert(tree._tree, value_); + function insert(Bytes32AVL storage tree, bytes32 key_, bytes calldata value_) internal { + _insert(tree._tree, key_, value_); } - function remove(Bytes32AVL storage tree, bytes32 value_) internal { - _remove(tree._tree, value_); + function remove(Bytes32AVL storage tree, bytes32 key_) internal { + _remove(tree._tree, key_); } - function search(Bytes32AVL storage tree, bytes32 value_) internal view returns (bool) { - return _search(tree._tree, value_); + function search(Bytes32AVL storage tree, bytes32 key_) internal view returns (bool) { + return _search(tree._tree, key_); + } + + function getValue( + Bytes32AVL storage tree, + bytes32 key_ + ) internal view returns (bytes storage) { + return tree._tree.tree[key_].value; + } + + function rangeQuery( + Bytes32AVL storage tree, + bytes32 min_, + bytes32 max_ + ) internal view returns (bytes32[] memory) { + require(min_ <= max_, "AvlTree: min should be less than or equal to max"); + + bytes32[] memory bytesResult_ = new bytes32[](tree._tree.treeSize); + uint256 count_ = _rangeQuery( + tree._tree.tree, + tree._tree.root, + min_, + max_, + bytesResult_, + 0, + _getComparator(tree._tree) + ); + + bytes32[] memory rangeResult_ = new bytes32[](count_); + for (uint256 i = 0; i < count_; i++) { + rangeResult_[i] = bytesResult_[i]; + } + + return rangeResult_; } function getMin(Bytes32AVL storage tree) internal view returns (bytes32) { @@ -162,28 +229,63 @@ library AvlTree { function setComparator( AddressAVL storage tree, - function(bytes32, bytes32) pure returns (int8) comparator_ + function(mapping(bytes32 => Node) storage, bytes32, bytes32) + view + returns (int8) comparator_ ) internal { _setComparator(tree._tree, comparator_); } - function insert(AddressAVL storage tree, address value_) internal { - _insert(tree._tree, bytes32(uint256(uint160(value_)))); + function insert(AddressAVL storage tree, address key_, bytes calldata value_) internal { + _insert(tree._tree, bytes32(uint256(uint160(key_))), value_); } - function remove(AddressAVL storage tree, address value_) internal { - _remove(tree._tree, bytes32(uint256(uint160(value_)))); + function remove(AddressAVL storage tree, address key_) internal { + _remove(tree._tree, bytes32(uint256(uint160(key_)))); } - function search(AddressAVL storage tree, address value_) private view returns (bool) { - return _search(tree._tree, bytes32(uint256(uint160(value_)))); + function search(AddressAVL storage tree, address key_) internal view returns (bool) { + return _search(tree._tree, bytes32(uint256(uint160(key_)))); + } + + function getValue( + AddressAVL storage tree, + address key_ + ) internal view returns (bytes storage) { + return tree._tree.tree[bytes32(uint256(uint160(key_)))].value; } - function getMin(AddressAVL storage tree) private view returns (address) { + function rangeQuery( + AddressAVL storage tree, + address min_, + address max_ + ) internal view returns (address[] memory) { + require(min_ <= max_, "AvlTree: min should be less than or equal to max"); + + bytes32[] memory bytesResult_ = new bytes32[](tree._tree.treeSize); + uint256 count_ = _rangeQuery( + tree._tree.tree, + tree._tree.root, + bytes32(uint256(uint160(min_))), + bytes32(uint256(uint160(max_))), + bytesResult_, + 0, + _getComparator(tree._tree) + ); + + address[] memory addressResult_ = new address[](count_); + for (uint256 i = 0; i < count_; i++) { + addressResult_[i] = address(uint160(uint256(bytesResult_[i]))); + } + + return addressResult_; + } + + function getMin(AddressAVL storage tree) internal view returns (address) { return address(uint160(uint256(_getMin(tree._tree.tree, tree._tree.root)))); } - function getMax(AddressAVL storage tree) private view returns (address) { + function getMax(AddressAVL storage tree) internal view returns (address) { return address(uint160(uint256(_getMax(tree._tree.tree, tree._tree.root)))); } @@ -240,22 +342,27 @@ library AvlTree { struct Node { bytes32 key; + bytes value; uint256 height; bytes32 left; bytes32 right; } struct Tree { - mapping(bytes32 => Node) tree; bytes32 root; uint256 treeSize; bool isCustomComparatorSet; - function(bytes32, bytes32) pure returns (int8) comparator; + mapping(bytes32 => Node) tree; + function(mapping(bytes32 => Node) storage, bytes32, bytes32) + view + returns (int8) comparator; } function _setComparator( Tree storage tree, - function(bytes32, bytes32) pure returns (int8) comparator_ + function(mapping(bytes32 => Node) storage, bytes32, bytes32) + view + returns (int8) comparator_ ) private { require(tree.treeSize == 0, "AvlTree: the tree must be empty"); @@ -264,15 +371,11 @@ library AvlTree { tree.comparator = comparator_; } - function _insert(Tree storage tree, bytes32 key_) private { + function _insert(Tree storage tree, bytes32 key_, bytes calldata value_) private { require(key_ != 0, "AvlTree: key is not allowed to be 0"); require(tree.tree[key_].key != key_, "AvlSegmentTree: the node already exists"); - function(bytes32, bytes32) pure returns (int8) comparator_ = tree.isCustomComparatorSet - ? tree.comparator - : _defaultComparator; - - tree.root = _insertNode(tree.tree, tree.root, key_, comparator_); + tree.root = _insertNode(tree.tree, tree.root, key_, value_, _getComparator(tree)); tree.treeSize++; } @@ -283,11 +386,7 @@ library AvlTree { require(tree.tree[key_].key == key_, "AvlSegmentTree: the node doesn't exist"); - function(bytes32, bytes32) pure returns (int8) comparator_ = tree.isCustomComparatorSet - ? tree.comparator - : _defaultComparator; - - tree.root = _removeNode(tree.tree, tree.root, key_, comparator_); + tree.root = _removeNode(tree.tree, tree.root, key_, _getComparator(tree)); tree.treeSize--; } @@ -296,18 +395,21 @@ library AvlTree { mapping(bytes32 => Node) storage _tree, bytes32 node_, bytes32 key_, - function(bytes32, bytes32) pure returns (int8) comparator_ + bytes calldata value_, + function(mapping(bytes32 => Node) storage, bytes32, bytes32) + view + returns (int8) comparator_ ) private returns (bytes32) { if (node_ == 0) { - _tree[key_] = Node({key: key_, left: 0, right: 0, height: 1}); + _tree[key_] = Node({key: key_, value: value_, left: 0, right: 0, height: 1}); return key_; } - if (comparator_(key_, node_) <= 0) { - _tree[node_].left = _insertNode(_tree, _tree[node_].left, key_, comparator_); + if (comparator_(_tree, key_, node_) <= 0) { + _tree[node_].left = _insertNode(_tree, _tree[node_].left, key_, value_, comparator_); } else { - _tree[node_].right = _insertNode(_tree, _tree[node_].right, key_, comparator_); + _tree[node_].right = _insertNode(_tree, _tree[node_].right, key_, value_, comparator_); } return _balance(_tree, node_); @@ -317,13 +419,15 @@ library AvlTree { mapping(bytes32 => Node) storage _tree, bytes32 node_, bytes32 key_, - function(bytes32, bytes32) pure returns (int8) comparator_ + function(mapping(bytes32 => Node) storage, bytes32, bytes32) + view + returns (int8) comparator_ ) private returns (bytes32) { - if (comparator_(key_, node_) == 0) { + if (comparator_(_tree, key_, node_) == 0) { bytes32 left_ = _tree[node_].left; bytes32 right_ = _tree[node_].right; - _tree[node_] = _tree[0]; + delete _tree[node_]; if (right_ == 0) { return left_; @@ -337,7 +441,7 @@ library AvlTree { _tree[temp_].left = left_; return _balance(_tree, temp_); - } else if (comparator_(key_, node_) < 0) { + } else if (comparator_(_tree, key_, node_) < 0) { _tree[node_].left = _removeNode(_tree, _tree[node_].left, key_, comparator_); } else { _tree[node_].right = _removeNode(_tree, _tree[node_].right, key_, comparator_); @@ -426,18 +530,73 @@ library AvlTree { } function _search(Tree storage tree, bytes32 key_) private view returns (bool) { - require(key_ != 0, "AvlTree: key is not allowed to be 0"); - require(tree.treeSize != 0, "AvlTree: tree is empty"); + return key_ != 0 && tree.tree[key_].key == key_; + } + + function _rangeQuery( + mapping(bytes32 => Node) storage _tree, + bytes32 node_, + bytes32 min_, + bytes32 max_, + bytes32[] memory result_, + uint256 index_, + function(mapping(bytes32 => Node) storage, bytes32, bytes32) + view + returns (int8) comparator_ + ) private view returns (uint256) { + if (node_ == 0) { + return index_; + } + + if (comparator_(_tree, node_, min_) >= 0 && comparator_(_tree, node_, max_) <= 0) { + index_ = _rangeQuery( + _tree, + _tree[node_].left, + min_, + max_, + result_, + index_, + comparator_ + ); + result_[index_++] = node_; + index_ = _rangeQuery( + _tree, + _tree[node_].right, + min_, + max_, + result_, + index_, + comparator_ + ); + } else if (comparator_(_tree, node_, min_) < 0) { + index_ = _rangeQuery( + _tree, + _tree[node_].right, + min_, + max_, + result_, + index_, + comparator_ + ); + } else { + index_ = _rangeQuery( + _tree, + _tree[node_].left, + min_, + max_, + result_, + index_, + comparator_ + ); + } - return tree.tree[key_].key == key_; + return index_; } function _getMin( mapping(bytes32 => Node) storage _tree, bytes32 node_ ) private view returns (bytes32) { - require(node_ != 0, "AvlTree: tree is empty"); - while (_tree[node_].left != 0) { node_ = _tree[node_].left; } @@ -449,8 +608,6 @@ library AvlTree { mapping(bytes32 => Node) storage _tree, bytes32 node_ ) private view returns (bytes32) { - require(node_ != 0, "AvlTree: tree is empty"); - while (_tree[node_].right != 0) { node_ = _tree[node_].right; } @@ -464,8 +621,6 @@ library AvlTree { view returns (uint256) traversalFunction_ ) private view returns (bytes32[] memory) { - require(tree.treeSize != 0, "AvlSegmentTreeMock: Tree is empty"); - bytes32[] memory keys_ = new bytes32[](tree.treeSize); traversalFunction_(tree.tree, tree.root, keys_, 0); @@ -534,7 +689,21 @@ library AvlTree { return index_; } - function _defaultComparator(bytes32 a, bytes32 b) private pure returns (int8) { + function _getComparator( + Tree storage tree + ) + private + view + returns (function(mapping(bytes32 => Node) storage, bytes32, bytes32) view returns (int8)) + { + return tree.isCustomComparatorSet ? tree.comparator : _defaultComparator; + } + + function _defaultComparator( + mapping(bytes32 => Node) storage, + bytes32 a, + bytes32 b + ) private pure returns (int8) { if (a < b) return -1; if (a > b) return 1; return 0; From 9402a365af4abd42157cf99c44075aed47a5e71c Mon Sep 17 00:00:00 2001 From: mllwchrry Date: Fri, 24 May 2024 18:07:46 +0300 Subject: [PATCH 03/11] added tests and natspec --- contracts/libs/data-structures/AvlTree.sol | 576 +++++++++------- .../mock/libs/data-structures/AvlTreeMock.sol | 272 ++++++++ package-lock.json | 292 ++++---- test/libs/data-structures/AvlTree.test.ts | 638 ++++++++++++++++++ 4 files changed, 1394 insertions(+), 384 deletions(-) create mode 100644 contracts/mock/libs/data-structures/AvlTreeMock.sol create mode 100644 test/libs/data-structures/AvlTree.test.ts diff --git a/contracts/libs/data-structures/AvlTree.sol b/contracts/libs/data-structures/AvlTree.sol index b286388a..7562e6dd 100644 --- a/contracts/libs/data-structures/AvlTree.sol +++ b/contracts/libs/data-structures/AvlTree.sol @@ -2,11 +2,53 @@ pragma solidity ^0.8.4; import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; +import {TypeCaster} from "../utils/TypeCaster.sol"; /** - * @notice The implementation of AVL tree. + * @notice AVL Tree module + * + * This library provides implementation of three sets with dynamic key types: + * `UintAVL`, `Bytes32AVL` and `Bytes32AVL`. + * + * Each element in the tree contains a bytes `value` field to allow storing different types + * of values including structs + * + * The implementation supports setting custom comparator function + * + * Gas usage for _insert and _remove functions (where count is the number of elements added to the tree): + * + * | Statistic | _insert | _remove | + * | --------- | ------------ | ---------------- | + * | count | 1000 | 1000 | + * | mean | 309,851 gas | 164,735 gas | + * | min | 162,211 gas | 48,691 gas | + * | max | 340,416 gas | 220,653 gas | + * + * ## Usage example: + * + * ``` + * using AvlTree for AvlTree.UintAVL; + * + * AvlTree.UintAVL internal uintTree; + * + * ................................................ + * + * uintTree.setComparator(comparatorFunction); + * + * uintTree.insert(1, abi.encode(1234)); + * + * uintTree.remove(1); + * + * uintTree.root(); + * + * uintTree.treeSize(); + * + * uintTree.inOrderTraversal(); + * ``` */ library AvlTree { + using TypeCaster for *; + /** ********************* * UintAVL * @@ -17,106 +59,141 @@ library AvlTree { Tree _tree; } + /** + * @notice The function to set a custom comparator function, that will be used to build the uint256 tree. + * @param tree self. + * @param comparator_ The function that accepts keys and values of the nodes to compare. + */ function setComparator( UintAVL storage tree, - function(mapping(bytes32 => Node) storage, bytes32, bytes32) - view - returns (int8) comparator_ + function(bytes32, bytes32, bytes memory, bytes memory) view returns (int8) comparator_ ) internal { _setComparator(tree._tree, comparator_); } - function insert(UintAVL storage tree, uint256 key_, bytes calldata value_) internal { + /** + * @notice The function to insert a node into the uint256 tree. + * Complexity is O(log(n)), where n is the number of elements in the tree. + * + * @param tree self. + * @param key_ the key to insert. + * @param value_ the value to insert. + */ + function insert(UintAVL storage tree, uint256 key_, bytes memory value_) internal { _insert(tree._tree, bytes32(key_), value_); } + /** + * @notice The function to remove a node from the uint256 tree. + * Complexity is O(log(n)), where n is the number of elements in the tree. + * + * @param tree self. + * @param key_ the key of the node to remove. + */ function remove(UintAVL storage tree, uint256 key_) internal { _remove(tree._tree, bytes32(key_)); } + /** + * @notice The function to search for a node in the uint256 tree. + * Complexity is O(1). + * + * @param tree self. + * @param key_ the key of the node to search for. + * @return True if the node exists, false otherwise. + */ function search(UintAVL storage tree, uint256 key_) internal view returns (bool) { return _search(tree._tree, bytes32(key_)); } + /** + * @notice The function to retrieve the value associated with a key in the uint256 tree. + * Complexity is O(1). + * + * @param tree self. + * @param key_ the key to get the value for. + * @return The value associated with the key. + */ function getValue(UintAVL storage tree, uint256 key_) internal view returns (bytes storage) { - return tree._tree.tree[bytes32(key_)].value; - } - - function rangeQuery( - UintAVL storage tree, - uint256 min_, - uint256 max_ - ) internal view returns (uint256[] memory) { - require(min_ <= max_, "AvlTree: min should be less than or equal to max"); - - bytes32[] memory bytesResult_ = new bytes32[](tree._tree.treeSize); - uint256 count_ = _rangeQuery( - tree._tree.tree, - tree._tree.root, - bytes32(min_), - bytes32(max_), - bytesResult_, - 0, - _getComparator(tree._tree) - ); - - uint256[] memory uintResult_ = new uint256[](count_); - for (uint256 i = 0; i < count_; i++) { - uintResult_[i] = uint256(bytesResult_[i]); - } + require(_search(tree._tree, bytes32(key_)), "AvlTree: node with such key doesn't exist"); - return uintResult_; + return tree._tree.tree[bytes32(key_)].value; } + /** + * @notice The function to retrieve the minimum key in the uint256 tree. + * Complexity is O(log(n)), where n is the number of elements in the tree. + * + * @param tree self. + * @return The minimum key in the tree. + */ function getMin(UintAVL storage tree) internal view returns (uint256) { return uint256(_getMin(tree._tree.tree, tree._tree.root)); } + /** + * @notice The function to retrieve the maximum key in the uint256 tree. + * Complexity is O(log(n)), where n is the number of elements in the tree. + * + * @param tree self. + * @return The maximum key in the tree. + */ function getMax(UintAVL storage tree) internal view returns (uint256) { return uint256(_getMax(tree._tree.tree, tree._tree.root)); } + /** + * @notice The function to return the key of the root element of the uint256 tree. + * Complexity is O(1). + * + * @param tree self. + * @return The key of the root element of the uint256 tree. + */ function root(UintAVL storage tree) internal view returns (uint256) { return uint256(tree._tree.root); } + /** + * @notice The function to retrieve the size of the uint256 tree. + * @param tree self. + * @return The size of the tree. + */ function treeSize(UintAVL storage tree) internal view returns (uint256) { return tree._tree.treeSize; } + /** + * @notice The function to perform an in-order traversal of the uint256 tree. + * @param tree self. + * @return An array of keys in in-order traversal. + */ function inOrderTraversal(UintAVL storage tree) internal view returns (uint256[] memory) { - bytes32[] memory bytesTraversal_ = _getTraversal(tree._tree, _inOrderTraversal); - uint256[] memory uintTraversal_ = new uint256[](bytesTraversal_.length); - - for (uint256 i = 0; i < bytesTraversal_.length; i++) { - uintTraversal_[i] = uint256(bytesTraversal_[i]); - } - - return uintTraversal_; + return _getTraversal(tree._tree, _inOrderTraversal).asUint256Array(); } + /** + * @notice The function to perform an pre-order traversal of the uint256 tree. + * @param tree self. + * @return An array of keys in pre-order traversal. + */ function preOrderTraversal(UintAVL storage tree) internal view returns (uint256[] memory) { - bytes32[] memory bytesTraversal_ = _getTraversal(tree._tree, _preOrderTraversal); - uint256[] memory uintTraversal_ = new uint256[](bytesTraversal_.length); - - for (uint256 i = 0; i < bytesTraversal_.length; i++) { - uintTraversal_[i] = uint256(bytesTraversal_[i]); - } - - return uintTraversal_; + return _getTraversal(tree._tree, _preOrderTraversal).asUint256Array(); } + /** + * @notice The function to perform an post-order traversal of the uint256 tree. + * @param tree self. + * @return An array of keys in post-order traversal. + */ function postOrderTraversal(UintAVL storage tree) internal view returns (uint256[] memory) { - bytes32[] memory bytesTraversal_ = _getTraversal(tree._tree, _postOrderTraversal); - uint256[] memory uintTraversal_ = new uint256[](bytesTraversal_.length); - - for (uint256 i = 0; i < bytesTraversal_.length; i++) { - uintTraversal_[i] = uint256(bytesTraversal_[i]); - } - - return uintTraversal_; + return _getTraversal(tree._tree, _postOrderTraversal).asUint256Array(); } + /** + * @notice The function to check whether the custom comparator function is set for the uint256 tree. + * @param tree self. + * @return True if the custom comparator function is set, false otherwise. + */ function isCustomComparatorSet(UintAVL storage tree) internal view returns (bool) { return tree._tree.isCustomComparatorSet; } @@ -131,88 +208,143 @@ library AvlTree { Tree _tree; } + /** + * @notice The function to set a custom comparator function, that will be used to build the byte32 tree. + * @param tree self. + * @param comparator_ The function that accepts keys and values of the nodes to compare. + */ function setComparator( Bytes32AVL storage tree, - function(mapping(bytes32 => Node) storage, bytes32, bytes32) - view - returns (int8) comparator_ + function(bytes32, bytes32, bytes memory, bytes memory) view returns (int8) comparator_ ) internal { _setComparator(tree._tree, comparator_); } - function insert(Bytes32AVL storage tree, bytes32 key_, bytes calldata value_) internal { + /** + * @notice The function to insert a node into the bytes32 tree. + * Complexity is O(log(n)), where n is the number of elements in the tree. + * + * @param tree self. + * @param key_ the key to insert. + * @param value_ the value to insert. + */ + function insert(Bytes32AVL storage tree, bytes32 key_, bytes memory value_) internal { _insert(tree._tree, key_, value_); } + /** + * @notice The function to remove a node from the bytes32 tree. + * Complexity is O(log(n)), where n is the number of elements in the tree. + * + * @param tree self. + * @param key_ the key of the node to remove. + */ function remove(Bytes32AVL storage tree, bytes32 key_) internal { _remove(tree._tree, key_); } + /** + * @notice The function to search for a node in the bytes32 tree. + * Complexity is O(1). + * + * @param tree self. + * @param key_ the key of the node to search for. + * @return True if the node exists, false otherwise. + */ function search(Bytes32AVL storage tree, bytes32 key_) internal view returns (bool) { return _search(tree._tree, key_); } + /** + * @notice The function to retrieve the value associated with a key in the bytes32 tree. + * Complexity is O(1). + * + * @param tree self. + * @param key_ the key to get the value for. + * @return The value associated with the key. + */ function getValue( Bytes32AVL storage tree, bytes32 key_ ) internal view returns (bytes storage) { + require(_search(tree._tree, key_), "AvlTree: node with such key doesn't exist"); return tree._tree.tree[key_].value; } - function rangeQuery( - Bytes32AVL storage tree, - bytes32 min_, - bytes32 max_ - ) internal view returns (bytes32[] memory) { - require(min_ <= max_, "AvlTree: min should be less than or equal to max"); - - bytes32[] memory bytesResult_ = new bytes32[](tree._tree.treeSize); - uint256 count_ = _rangeQuery( - tree._tree.tree, - tree._tree.root, - min_, - max_, - bytesResult_, - 0, - _getComparator(tree._tree) - ); - - bytes32[] memory rangeResult_ = new bytes32[](count_); - for (uint256 i = 0; i < count_; i++) { - rangeResult_[i] = bytesResult_[i]; - } - - return rangeResult_; - } - + /** + * @notice The function to retrieve the minimum key in the bytes32 tree. + * Complexity is O(log(n)), where n is the number of elements in the tree. + * + * @param tree self. + * @return The minimum key in the tree. + */ function getMin(Bytes32AVL storage tree) internal view returns (bytes32) { return _getMin(tree._tree.tree, tree._tree.root); } + /** + * @notice The function to retrieve the maximum key in the bytes32 tree. + * Complexity is O(log(n)), where n is the number of elements in the tree. + * + * @param tree self. + * @return The maximum key in the tree. + */ function getMax(Bytes32AVL storage tree) internal view returns (bytes32) { return _getMax(tree._tree.tree, tree._tree.root); } + /** + * @notice The function to return the key of the root element of the bytes32 tree. + * Complexity is O(1). + * + * @param tree self. + * @return The key of the root element of the uint256 tree. + */ function root(Bytes32AVL storage tree) internal view returns (bytes32) { return tree._tree.root; } + /** + * @notice The function to retrieve the size of the bytes32 tree. + * @param tree self. + * @return The size of the tree. + */ function treeSize(Bytes32AVL storage tree) internal view returns (uint256) { return tree._tree.treeSize; } + /** + * @notice The function to perform an in-order traversal of the bytes32 tree. + * @param tree self. + * @return An array of keys in in-order traversal. + */ function inOrderTraversal(Bytes32AVL storage tree) internal view returns (bytes32[] memory) { return _getTraversal(tree._tree, _inOrderTraversal); } + /** + * @notice The function to perform an pre-order traversal of the bytes32 tree. + * @param tree self. + * @return An array of keys in pre-order traversal. + */ function preOrderTraversal(Bytes32AVL storage tree) internal view returns (bytes32[] memory) { return _getTraversal(tree._tree, _preOrderTraversal); } + /** + * @notice The function to perform an post-order traversal of the bytes32 tree. + * @param tree self. + * @return An array of keys in post-order traversal. + */ function postOrderTraversal(Bytes32AVL storage tree) internal view returns (bytes32[] memory) { return _getTraversal(tree._tree, _postOrderTraversal); } + /** + * @notice The function to check whether the custom comparator function is set for the bytes32 tree. + * @param tree self. + * @return True if the custom comparator function is set, false otherwise. + */ function isCustomComparatorSet(Bytes32AVL storage tree) internal view returns (bool) { return tree._tree.isCustomComparatorSet; } @@ -227,109 +359,146 @@ library AvlTree { Tree _tree; } + /** + * @notice The function to set a custom comparator function, that will be used to build the address tree. + * @param tree self. + * @param comparator_ The function that accepts keys and values of the nodes to compare. + */ function setComparator( AddressAVL storage tree, - function(mapping(bytes32 => Node) storage, bytes32, bytes32) - view - returns (int8) comparator_ + function(bytes32, bytes32, bytes memory, bytes memory) view returns (int8) comparator_ ) internal { _setComparator(tree._tree, comparator_); } - function insert(AddressAVL storage tree, address key_, bytes calldata value_) internal { - _insert(tree._tree, bytes32(uint256(uint160(key_))), value_); + /** + * @notice The function to insert a node into the address tree. + * Complexity is O(log(n)), where n is the number of elements in the tree. + * + * @param tree self. + * @param key_ The key to insert. + * @param value_ The value to insert. + */ + function insert(AddressAVL storage tree, address key_, bytes memory value_) internal { + _insert(tree._tree, _asBytes32(key_), value_); } + /** + * @notice The function to remove a node from the address tree. + * Complexity is O(log(n)), where n is the number of elements in the tree. + * + * @param tree self. + * @param key_ the key of the node to remove. + */ function remove(AddressAVL storage tree, address key_) internal { - _remove(tree._tree, bytes32(uint256(uint160(key_)))); + _remove(tree._tree, _asBytes32(key_)); } + /** + * @notice The function to search for a node in the address tree. + * Complexity is O(1). + * + * @param tree self. + * @param key_ the key of the node to search for. + * @return True if the node exists, false otherwise. + */ function search(AddressAVL storage tree, address key_) internal view returns (bool) { - return _search(tree._tree, bytes32(uint256(uint160(key_)))); + return _search(tree._tree, _asBytes32(key_)); } + /** + * @notice The function to retrieve the value associated with a key in the address tree. + * Complexity is O(1). + * + * @param tree self. + * @param key_ the key to get the value for. + * @return The value associated with the key. + */ function getValue( AddressAVL storage tree, address key_ ) internal view returns (bytes storage) { - return tree._tree.tree[bytes32(uint256(uint160(key_)))].value; - } - - function rangeQuery( - AddressAVL storage tree, - address min_, - address max_ - ) internal view returns (address[] memory) { - require(min_ <= max_, "AvlTree: min should be less than or equal to max"); - - bytes32[] memory bytesResult_ = new bytes32[](tree._tree.treeSize); - uint256 count_ = _rangeQuery( - tree._tree.tree, - tree._tree.root, - bytes32(uint256(uint160(min_))), - bytes32(uint256(uint160(max_))), - bytesResult_, - 0, - _getComparator(tree._tree) + require( + _search(tree._tree, _asBytes32(key_)), + "AvlTree: node with such key doesn't exist" ); - - address[] memory addressResult_ = new address[](count_); - for (uint256 i = 0; i < count_; i++) { - addressResult_[i] = address(uint160(uint256(bytesResult_[i]))); - } - - return addressResult_; + return tree._tree.tree[_asBytes32(key_)].value; } + /** + * @notice The function to retrieve the minimum key in the address tree. + * Complexity is O(log(n)), where n is the number of elements in the tree. + * + * @param tree self. + * @return The minimum key in the tree. + */ function getMin(AddressAVL storage tree) internal view returns (address) { - return address(uint160(uint256(_getMin(tree._tree.tree, tree._tree.root)))); + return _asAddress(_getMin(tree._tree.tree, tree._tree.root)); } + /** + * @notice The function to retrieve the maximum key in the address tree. + * Complexity is O(log(n)), where n is the number of elements in the tree. + * + * @param tree self. + * @return The maximum key in the tree. + */ function getMax(AddressAVL storage tree) internal view returns (address) { - return address(uint160(uint256(_getMax(tree._tree.tree, tree._tree.root)))); + return _asAddress(_getMax(tree._tree.tree, tree._tree.root)); } + /** + * @notice The function to return the key of the root element of the address tree. + * Complexity is O(1). + * + * @param tree self. + * @return The key of the root element of the uint256 tree. + */ function root(AddressAVL storage tree) internal view returns (address) { - return address(uint160(uint256(tree._tree.root))); + return _asAddress(tree._tree.root); } + /** + * @notice The function to retrieve the size of the address tree. + * @param tree self. + * @return The size of the tree. + */ function treeSize(AddressAVL storage tree) internal view returns (uint256) { return tree._tree.treeSize; } + /** + * @notice The function to perform an in-order traversal of the address tree. + * @param tree self. + * @return An array of keys in in-order traversal. + */ function inOrderTraversal(AddressAVL storage tree) internal view returns (address[] memory) { - bytes32[] memory bytesTraversal_ = _getTraversal(tree._tree, _inOrderTraversal); - address[] memory addressTraversal_ = new address[](bytesTraversal_.length); - - for (uint256 i = 0; i < bytesTraversal_.length; i++) { - addressTraversal_[i] = address(uint160(uint256(bytesTraversal_[i]))); - } - - return addressTraversal_; + return _getTraversal(tree._tree, _inOrderTraversal).asAddressArray(); } + /** + * @notice The function to perform an pre-order traversal of the address tree. + * @param tree self. + * @return An array of keys in pre-order traversal. + */ function preOrderTraversal(AddressAVL storage tree) internal view returns (address[] memory) { - bytes32[] memory bytesTraversal_ = _getTraversal(tree._tree, _preOrderTraversal); - address[] memory addressTraversal_ = new address[](bytesTraversal_.length); - - for (uint256 i = 0; i < bytesTraversal_.length; i++) { - addressTraversal_[i] = address(uint160(uint256(bytesTraversal_[i]))); - } - - return addressTraversal_; + return _getTraversal(tree._tree, _preOrderTraversal).asAddressArray(); } + /** + * @notice The function to perform an post-order traversal of the address tree. + * @param tree self. + * @return An array of keys in post-order traversal. + */ function postOrderTraversal(AddressAVL storage tree) internal view returns (address[] memory) { - bytes32[] memory bytesTraversal_ = _getTraversal(tree._tree, _postOrderTraversal); - address[] memory addressTraversal_ = new address[](bytesTraversal_.length); - - for (uint256 i = 0; i < bytesTraversal_.length; i++) { - addressTraversal_[i] = address(uint160(uint256(bytesTraversal_[i]))); - } - - return addressTraversal_; + return _getTraversal(tree._tree, _postOrderTraversal).asAddressArray(); } + /** + * @notice The function to check whether the custom comparator function is set for the address tree. + * @param tree self. + * @return True if the custom comparator function is set, false otherwise. + */ function isCustomComparatorSet(AddressAVL storage tree) internal view returns (bool) { return tree._tree.isCustomComparatorSet; } @@ -353,16 +522,12 @@ library AvlTree { uint256 treeSize; bool isCustomComparatorSet; mapping(bytes32 => Node) tree; - function(mapping(bytes32 => Node) storage, bytes32, bytes32) - view - returns (int8) comparator; + function(bytes32, bytes32, bytes memory, bytes memory) view returns (int8) comparator; } function _setComparator( Tree storage tree, - function(mapping(bytes32 => Node) storage, bytes32, bytes32) - view - returns (int8) comparator_ + function(bytes32, bytes32, bytes memory, bytes memory) view returns (int8) comparator_ ) private { require(tree.treeSize == 0, "AvlTree: the tree must be empty"); @@ -371,9 +536,9 @@ library AvlTree { tree.comparator = comparator_; } - function _insert(Tree storage tree, bytes32 key_, bytes calldata value_) private { + function _insert(Tree storage tree, bytes32 key_, bytes memory value_) private { require(key_ != 0, "AvlTree: key is not allowed to be 0"); - require(tree.tree[key_].key != key_, "AvlSegmentTree: the node already exists"); + require(tree.tree[key_].key != key_, "AvlTree: the node already exists"); tree.root = _insertNode(tree.tree, tree.root, key_, value_, _getComparator(tree)); @@ -382,9 +547,7 @@ library AvlTree { function _remove(Tree storage tree, bytes32 key_) private { require(key_ != 0, "AvlTree: key is not allowed to be 0"); - require(tree.treeSize != 0, "AvlSegmentTree: tree is empty"); - - require(tree.tree[key_].key == key_, "AvlSegmentTree: the node doesn't exist"); + require(tree.tree[key_].key == key_, "AvlTree: the node doesn't exist"); tree.root = _removeNode(tree.tree, tree.root, key_, _getComparator(tree)); @@ -395,10 +558,8 @@ library AvlTree { mapping(bytes32 => Node) storage _tree, bytes32 node_, bytes32 key_, - bytes calldata value_, - function(mapping(bytes32 => Node) storage, bytes32, bytes32) - view - returns (int8) comparator_ + bytes memory value_, + function(bytes32, bytes32, bytes memory, bytes memory) view returns (int8) comparator_ ) private returns (bytes32) { if (node_ == 0) { _tree[key_] = Node({key: key_, value: value_, left: 0, right: 0, height: 1}); @@ -406,7 +567,7 @@ library AvlTree { return key_; } - if (comparator_(_tree, key_, node_) <= 0) { + if (comparator_(key_, node_, value_, _tree[node_].value) <= 0) { _tree[node_].left = _insertNode(_tree, _tree[node_].left, key_, value_, comparator_); } else { _tree[node_].right = _insertNode(_tree, _tree[node_].right, key_, value_, comparator_); @@ -419,11 +580,9 @@ library AvlTree { mapping(bytes32 => Node) storage _tree, bytes32 node_, bytes32 key_, - function(mapping(bytes32 => Node) storage, bytes32, bytes32) - view - returns (int8) comparator_ + function(bytes32, bytes32, bytes memory, bytes memory) view returns (int8) comparator_ ) private returns (bytes32) { - if (comparator_(_tree, key_, node_) == 0) { + if (comparator_(key_, node_, _tree[key_].value, _tree[node_].value) == 0) { bytes32 left_ = _tree[node_].left; bytes32 right_ = _tree[node_].right; @@ -441,7 +600,7 @@ library AvlTree { _tree[temp_].left = left_; return _balance(_tree, temp_); - } else if (comparator_(_tree, key_, node_) < 0) { + } else if (comparator_(key_, node_, _tree[key_].value, _tree[node_].value) < 0) { _tree[node_].left = _removeNode(_tree, _tree[node_].left, key_, comparator_); } else { _tree[node_].right = _removeNode(_tree, _tree[node_].right, key_, comparator_); @@ -533,66 +692,6 @@ library AvlTree { return key_ != 0 && tree.tree[key_].key == key_; } - function _rangeQuery( - mapping(bytes32 => Node) storage _tree, - bytes32 node_, - bytes32 min_, - bytes32 max_, - bytes32[] memory result_, - uint256 index_, - function(mapping(bytes32 => Node) storage, bytes32, bytes32) - view - returns (int8) comparator_ - ) private view returns (uint256) { - if (node_ == 0) { - return index_; - } - - if (comparator_(_tree, node_, min_) >= 0 && comparator_(_tree, node_, max_) <= 0) { - index_ = _rangeQuery( - _tree, - _tree[node_].left, - min_, - max_, - result_, - index_, - comparator_ - ); - result_[index_++] = node_; - index_ = _rangeQuery( - _tree, - _tree[node_].right, - min_, - max_, - result_, - index_, - comparator_ - ); - } else if (comparator_(_tree, node_, min_) < 0) { - index_ = _rangeQuery( - _tree, - _tree[node_].right, - min_, - max_, - result_, - index_, - comparator_ - ); - } else { - index_ = _rangeQuery( - _tree, - _tree[node_].left, - min_, - max_, - result_, - index_, - comparator_ - ); - } - - return index_; - } - function _getMin( mapping(bytes32 => Node) storage _tree, bytes32 node_ @@ -694,18 +793,31 @@ library AvlTree { ) private view - returns (function(mapping(bytes32 => Node) storage, bytes32, bytes32) view returns (int8)) + returns (function(bytes32, bytes32, bytes memory, bytes memory) view returns (int8)) { return tree.isCustomComparatorSet ? tree.comparator : _defaultComparator; } function _defaultComparator( - mapping(bytes32 => Node) storage, - bytes32 a, - bytes32 b + bytes32 key1_, + bytes32 key2_, + bytes memory, + bytes memory ) private pure returns (int8) { - if (a < b) return -1; - if (a > b) return 1; + if (key1_ < key2_) return -1; + if (key1_ > key2_) return 1; return 0; } + + function _asAddress(bytes32 from_) private pure returns (address to_) { + assembly { + to_ := from_ + } + } + + function _asBytes32(address from_) private pure returns (bytes32 to_) { + assembly { + to_ := from_ + } + } } diff --git a/contracts/mock/libs/data-structures/AvlTreeMock.sol b/contracts/mock/libs/data-structures/AvlTreeMock.sol new file mode 100644 index 00000000..ad51234d --- /dev/null +++ b/contracts/mock/libs/data-structures/AvlTreeMock.sol @@ -0,0 +1,272 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.4; + +import {AvlTree} from "../../../libs/data-structures/AvlTree.sol"; + +contract AvlTreeMock { + using AvlTree for *; + + struct customStruct { + uint256 valueUint; + bool valueBool; + string valueString; + } + + AvlTree.UintAVL internal _uintTree; + AvlTree.Bytes32AVL internal _bytes32Tree; + AvlTree.AddressAVL internal _addressTree; + + function setUintDescComparator() external { + _uintTree.setComparator(_descComparator); + } + + function setUintValueComparatorUint() external { + _uintTree.setComparator(_valueComparatorUint); + } + + function setUintValueComparatorString() external { + _uintTree.setComparator(_valueComparatorString); + } + + function setUintValueComparatorStruct() external { + _uintTree.setComparator(_structComparator); + } + + function setBytes32DescComparator() external { + _bytes32Tree.setComparator(_descComparator); + } + + function setUintValueComparatorAddress() external { + _addressTree.setComparator(_valueComparatorUint); + } + + function setAddressDescComparator() external { + _addressTree.setComparator(_descComparator); + } + + function insertUintToUint(uint256 key_, uint256 value_) external { + _uintTree.insert(key_, abi.encode(value_)); + } + + function insertAddressToUint(uint256 key_, address value_) external { + _uintTree.insert(key_, abi.encode(value_)); + } + + function insertStringToUint(uint256 key_, string calldata value_) external { + _uintTree.insert(key_, abi.encode(value_)); + } + + function insertStructToUint(uint256 key_, bytes calldata structBytes_) external { + _uintTree.insert(key_, structBytes_); + } + + function insertUintToBytes32(bytes32 key_, uint256 value_) external { + _bytes32Tree.insert(key_, abi.encode(value_)); + } + + function insertUintToAddress(address key_, uint256 value_) external { + _addressTree.insert(key_, abi.encode(value_)); + } + + function removeUint(uint256 key_) external { + _uintTree.remove(key_); + } + + function removeBytes32(bytes32 key_) external { + _bytes32Tree.remove(key_); + } + + function removeAddress(address key_) external { + _addressTree.remove(key_); + } + + function searchUint(uint256 key_) external view returns (bool) { + return _uintTree.search(key_); + } + + function searchBytes32(bytes32 key_) external view returns (bool) { + return _bytes32Tree.search(key_); + } + + function searchAddress(address key_) external view returns (bool) { + return _addressTree.search(key_); + } + + function getUintValueUint(uint256 key_) external view returns (uint256) { + return abi.decode(_uintTree.getValue(key_), (uint256)); + } + + function getAddressValueUint(uint256 key_) external view returns (address) { + return abi.decode(_uintTree.getValue(key_), (address)); + } + + function getStringValueUint(uint256 key_) external view returns (string memory) { + return abi.decode(_uintTree.getValue(key_), (string)); + } + + function getUintValueBytes32(bytes32 key_) external view returns (uint256) { + return abi.decode(_bytes32Tree.getValue(key_), (uint256)); + } + + function getUintValueAddress(address key_) external view returns (uint256) { + return abi.decode(_addressTree.getValue(key_), (uint256)); + } + + function getMinUint() external view returns (uint256) { + return _uintTree.getMin(); + } + + function getMinBytes32() external view returns (bytes32) { + return _bytes32Tree.getMin(); + } + + function getMinAddress() external view returns (address) { + return _addressTree.getMin(); + } + + function getMaxUint() external view returns (uint256) { + return _uintTree.getMax(); + } + + function getMaxBytes32() external view returns (bytes32) { + return _bytes32Tree.getMax(); + } + + function getMaxAddress() external view returns (address) { + return _addressTree.getMax(); + } + + function rootUint() external view returns (uint256) { + return _uintTree.root(); + } + + function rootBytes32() external view returns (bytes32) { + return _bytes32Tree.root(); + } + + function rootAddress() external view returns (address) { + return _addressTree.root(); + } + + function treeSizeUint() external view returns (uint256) { + return _uintTree.treeSize(); + } + + function treeSizeBytes32() external view returns (uint256) { + return _bytes32Tree.treeSize(); + } + + function treeSizeAddress() external view returns (uint256) { + return _addressTree.treeSize(); + } + + function inOrderTraversalUint() external view returns (uint256[] memory) { + return _uintTree.inOrderTraversal(); + } + + function inOrderTraversalBytes32() external view returns (bytes32[] memory) { + return _bytes32Tree.inOrderTraversal(); + } + + function inOrderTraversalAddress() external view returns (address[] memory) { + return _addressTree.inOrderTraversal(); + } + + function preOrderTraversalUint() external view returns (uint256[] memory) { + return _uintTree.preOrderTraversal(); + } + + function preOrderTraversalBytes32() external view returns (bytes32[] memory) { + return _bytes32Tree.preOrderTraversal(); + } + + function preOrderTraversalAddress() external view returns (address[] memory) { + return _addressTree.preOrderTraversal(); + } + + function postOrderTraversalUint() external view returns (uint256[] memory) { + return _uintTree.postOrderTraversal(); + } + + function postOrderTraversalBytes32() external view returns (bytes32[] memory) { + return _bytes32Tree.postOrderTraversal(); + } + + function postOrderTraversalAddress() external view returns (address[] memory) { + return _addressTree.postOrderTraversal(); + } + + function isCustomComparatorSetUint() external view returns (bool) { + return _uintTree.isCustomComparatorSet(); + } + + function isCustomComparatorSetBytes32() external view returns (bool) { + return _bytes32Tree.isCustomComparatorSet(); + } + + function isCustomComparatorSetAddress() external view returns (bool) { + return _addressTree.isCustomComparatorSet(); + } + + function valueToBytesStruct(uint256 value_) external pure returns (bytes memory) { + return abi.encode(customStruct(value_, true, "test")); + } + + function _descComparator( + bytes32 key1_, + bytes32 key2_, + bytes memory, + bytes memory + ) private pure returns (int8) { + if (key1_ > key2_) return -1; + if (key1_ < key2_) return 1; + return 0; + } + + function _valueComparatorUint( + bytes32, + bytes32, + bytes memory value1_, + bytes memory value2_ + ) private pure returns (int8) { + uint256 aValue = abi.decode(value1_, (uint256)); + uint256 bValue = abi.decode(value2_, (uint256)); + + if (aValue < bValue) return -1; + if (aValue > bValue) return 1; + return 0; + } + + function _valueComparatorString( + bytes32, + bytes32, + bytes memory value1_, + bytes memory value2_ + ) private pure returns (int8) { + uint256 minLength = value1_.length; + if (value2_.length < minLength) minLength = value2_.length; + + for (uint i = 0; i < minLength; i++) { + if (value1_[i] < value2_[i]) return -1; + else if (value1_[i] > value2_[i]) return 1; + } + + if (value1_.length < value2_.length) return -1; + else if (value1_.length > value2_.length) return 1; + else return 0; + } + + function _structComparator( + bytes32, + bytes32, + bytes memory value1_, + bytes memory value2_ + ) private pure returns (int8) { + customStruct memory aStruct_ = abi.decode(value1_, (customStruct)); + customStruct memory bStruct_ = abi.decode(value2_, (customStruct)); + + if (aStruct_.valueUint < bStruct_.valueUint) return -1; + if (aStruct_.valueUint > bStruct_.valueUint) return 1; + return 0; + } +} diff --git a/package-lock.json b/package-lock.json index 88cf618c..be4355fa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -72,21 +72,21 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.5.tgz", + "integrity": "sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.24.2", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.2.tgz", - "integrity": "sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.5.tgz", + "integrity": "sha512-8lLmua6AVh/8SLJRRVD6V8p73Hir9w5mJrhE+IPpILG31KKlI9iz5zmBYKcWPS59qSfgP9RaSBQSHHE81WKuEw==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.22.20", + "@babel/helper-validator-identifier": "^7.24.5", "chalk": "^2.4.2", "js-tokens": "^4.0.0", "picocolors": "^1.0.0" @@ -1056,17 +1056,17 @@ } }, "node_modules/@metamask/eth-sig-util": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@metamask/eth-sig-util/-/eth-sig-util-7.0.1.tgz", - "integrity": "sha512-59GSrMyFH2fPfu7nKeIQdZ150zxXNNhAQIUaFRUW+MGtVA4w/ONbiQobcRBLi+jQProfIyss51G8pfLPcQ0ylg==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@metamask/eth-sig-util/-/eth-sig-util-7.0.2.tgz", + "integrity": "sha512-DhTDMNEtED0ihIc4Tysm6qUJTvArCdgSTeeJWdo526W/cAk5mrSAvEYYgv8idAiBumDtcPWGimMTaB7MvY64bg==", "dev": true, "dependencies": { "@ethereumjs/util": "^8.1.0", "@metamask/abi-utils": "^2.0.2", "@metamask/utils": "^8.1.0", + "@scure/base": "~1.1.3", "ethereum-cryptography": "^2.1.2", - "tweetnacl": "^1.0.3", - "tweetnacl-util": "^0.15.1" + "tweetnacl": "^1.0.3" }, "engines": { "node": "^16.20 || ^18.16 || >=20" @@ -1617,9 +1617,9 @@ } }, "node_modules/@nomicfoundation/hardhat-ethers": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-ethers/-/hardhat-ethers-3.0.5.tgz", - "integrity": "sha512-RNFe8OtbZK6Ila9kIlHp0+S80/0Bu/3p41HUpaRIoHLm6X3WekTd83vob3rE54Duufu1edCiBDxspBzi2rxHHw==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-ethers/-/hardhat-ethers-3.0.6.tgz", + "integrity": "sha512-/xzkFQAaHQhmIAYOQmvHBPwL+NkwLzT9gRZBsgWUYeV+E6pzXsBQsHfRYbAZ3XEYare+T7S+5Tg/1KDJgepSkA==", "dev": true, "dependencies": { "debug": "^4.1.1", @@ -2141,9 +2141,9 @@ } }, "node_modules/@tsconfig/node10": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.10.tgz", - "integrity": "sha512-PiaIWIoPvO6qm6t114ropMCagj6YAF24j9OkCA2mJDXFnlionEwhsBCJ8yek4aib575BI3OkART/90WsgHgLWw==", + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", "dev": true }, "node_modules/@tsconfig/node12": { @@ -2204,9 +2204,9 @@ } }, "node_modules/@types/chai": { - "version": "4.3.14", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.14.tgz", - "integrity": "sha512-Wj71sXE4Q4AkGdG9Tvq1u/fquNz9EdG4LIJMwVVII7ashjD/8cf8fyIfJAjRr6YcsXnSE8cOGQPq1gqeR8z+3w==", + "version": "4.3.16", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.16.tgz", + "integrity": "sha512-PatH4iOdyh3MyWtmHVFXLWCCIhUbopaltqddG9BzB+gMIzee2MJrvd+jouii9Z3wzQJruGWAm7WOMjgfG8hQlQ==", "dev": true }, "node_modules/@types/chai-as-promised": { @@ -2268,9 +2268,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "18.19.26", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.26.tgz", - "integrity": "sha512-+wiMJsIwLOYCvUqSdKTrfkS8mpTp+MPINe6+Np4TAGFWWRWiBQ5kSq9nZGCSPkzx9mvT+uEukzpX4MOSCydcvw==", + "version": "18.19.33", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.33.tgz", + "integrity": "sha512-NR9+KrpSajr2qBVp/Yt5TU/rp+b5Mayi3+OlMlcg2cVCfRmcG5PWZ7S4+MG9PZ5gWBoc9Pd0BKSRViuBCRPu0A==", "dev": true, "dependencies": { "undici-types": "~5.26.4" @@ -2727,9 +2727,9 @@ } }, "node_modules/axios": { - "version": "1.6.8", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz", - "integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==", + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz", + "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==", "dev": true, "dependencies": { "follow-redirects": "^1.15.6", @@ -2961,12 +2961,12 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -2978,6 +2978,12 @@ "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", "dev": true }, + "node_modules/brotli-wasm": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brotli-wasm/-/brotli-wasm-2.0.1.tgz", + "integrity": "sha512-+3USgYsC7bzb5yU0/p2HnnynZl0ak0E6uoIm4UW4Aby/8s8HFCq6NCfrrf1E9c3O8OCSzq3oYO1tUVqIi61Nww==", + "dev": true + }, "node_modules/browser-stdout": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", @@ -3131,15 +3137,15 @@ } }, "node_modules/chai-as-promised": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.1.tgz", - "integrity": "sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.2.tgz", + "integrity": "sha512-aBDHZxRzYnUYuIAIPBH2s511DjlKPzXNlXSGFC8CwmroWQLfrW0LtE1nK3MAwwNhJPa9raEjNCmRoFpG0Hurdw==", "dev": true, "dependencies": { "check-error": "^1.0.2" }, "peerDependencies": { - "chai": ">= 2.1.2 < 5" + "chai": ">= 2.1.2 < 6" } }, "node_modules/chalk": { @@ -3299,9 +3305,9 @@ } }, "node_modules/cli-table3": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.4.tgz", - "integrity": "sha512-Lm3L0p+/npIQWNIiyF/nAn7T5dnOwR3xNTHXYEBFBFVPXzCVNZ5lqEC/1eo/EVfpDsQ1I+TX4ORPQgp+UI0CRw==", + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", + "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", "dev": true, "dependencies": { "string-width": "^4.2.0" @@ -3437,9 +3443,9 @@ } }, "node_modules/core-js": { - "version": "3.36.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.36.1.tgz", - "integrity": "sha512-BTvUrwxVBezj5SZ3f10ImnX2oRByMxql3EimVqMysepbC9EeMUOpLwdy6Eoili2x6E4kf+ZUB5k/+Jv55alPfA==", + "version": "3.37.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.37.1.tgz", + "integrity": "sha512-Xn6qmxrQZyB0FFY8E3bgRXei3lWDJHhvI+u0q9TKIYM49G8pAr0FgnnrFRAmsbptZL1yxRADVXn+x5AGsbBfyw==", "dev": true, "hasInstallScript": true, "funding": { @@ -3879,9 +3885,9 @@ } }, "node_modules/es-abstract": { - "version": "1.23.2", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.2.tgz", - "integrity": "sha512-60s3Xv2T2p1ICykc7c+DNDPLDMm9t4QxCOUU0K9JxiLjM3C1zB9YVdN7tjxrFd4+AkZ8CdX1ovUga4P2+1e+/w==", + "version": "1.23.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", + "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", "dev": true, "dependencies": { "array-buffer-byte-length": "^1.0.1", @@ -3923,11 +3929,11 @@ "safe-regex-test": "^1.0.3", "string.prototype.trim": "^1.2.9", "string.prototype.trimend": "^1.0.8", - "string.prototype.trimstart": "^1.0.7", + "string.prototype.trimstart": "^1.0.8", "typed-array-buffer": "^1.0.2", "typed-array-byte-length": "^1.0.1", "typed-array-byte-offset": "^1.0.2", - "typed-array-length": "^1.0.5", + "typed-array-length": "^1.0.6", "unbox-primitive": "^1.0.2", "which-typed-array": "^1.1.15" }, @@ -4083,12 +4089,12 @@ } }, "node_modules/ethereum-bloom-filters": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/ethereum-bloom-filters/-/ethereum-bloom-filters-1.0.10.tgz", - "integrity": "sha512-rxJ5OFN3RwjQxDcFP2Z5+Q9ho4eIdEmSc2ht0fCu8Se9nbXjZ7/031uXoUYJ87KHCOdVeiUuwSnoS7hmYAGVHA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ethereum-bloom-filters/-/ethereum-bloom-filters-1.1.0.tgz", + "integrity": "sha512-J1gDRkLpuGNvWYzWslBQR9cDV4nd4kfvVTE/Wy4Kkm4yb3EYRSlyi0eB/inTsSTTVyA0+HyzHgbr95Fn/Z1fSw==", "dev": true, "dependencies": { - "js-sha3": "^0.8.0" + "@noble/hashes": "^1.4.0" } }, "node_modules/ethereum-cryptography": { @@ -4218,9 +4224,9 @@ } }, "node_modules/ethers": { - "version": "6.11.1", - "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.11.1.tgz", - "integrity": "sha512-mxTAE6wqJQAbp5QAe/+o+rXOID7Nw91OZXvgpjDa1r4fAbq2Nu314oEZSbjoRLacuCzs7kUC3clEvkCQowffGg==", + "version": "6.12.1", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.12.1.tgz", + "integrity": "sha512-j6wcVoZf06nqEcBbDWkKg8Fp895SS96dSnTCjiXT+8vt2o02raTn4Lo9ERUuIVU5bAjoPYeA+7ytQFexFmLuVw==", "dev": true, "funding": [ { @@ -4380,9 +4386,9 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "dependencies": { "to-regex-range": "^5.0.1" @@ -4717,12 +4723,13 @@ } }, "node_modules/globalthis": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", "dev": true, "dependencies": { - "define-properties": "^1.1.3" + "define-properties": "^1.2.1", + "gopd": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -4981,9 +4988,9 @@ } }, "node_modules/hardhat-gas-reporter": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hardhat-gas-reporter/-/hardhat-gas-reporter-2.0.2.tgz", - "integrity": "sha512-i/+g+dX+/+MZ7L4M5NE78TgjDgnE3dINhsNUJmjwbqR3cLSMPrsEyiee+/1c5w32JSD08SKyDf+8W9Q5iC+zLw==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/hardhat-gas-reporter/-/hardhat-gas-reporter-2.2.0.tgz", + "integrity": "sha512-eAlLWnyDpQ+wJXgSCZsM0yt+rQm3ryJia1I1Hoi94LzlIfuSPcsMQM12VO6UHmAFLvXvoKxXPJ3ZYk0Kz+7CDQ==", "dev": true, "dependencies": { "@ethersproject/abi": "^5.7.0", @@ -4991,6 +4998,7 @@ "@ethersproject/units": "^5.7.0", "@solidity-parser/parser": "^0.18.0", "axios": "^1.6.7", + "brotli-wasm": "^2.0.1", "chalk": "4.1.2", "cli-table3": "^0.6.3", "ethereum-cryptography": "^2.1.3", @@ -5064,22 +5072,22 @@ "dev": true }, "node_modules/hardhat-gas-reporter/node_modules/glob": { - "version": "10.3.10", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", - "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.1.tgz", + "integrity": "sha512-2jelhlq3E4ho74ZyVLN03oKdAZVUa6UDZzFLVH1H7dnoax+y9qyaq8zBkfDIggjniU19z0wU18y16jMB2eyVIw==", "dev": true, "dependencies": { "foreground-child": "^3.1.0", - "jackspeak": "^2.3.5", - "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", - "path-scurry": "^1.10.1" + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=16 || 14 >=14.18" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -5095,9 +5103,9 @@ } }, "node_modules/hardhat-gas-reporter/node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", "dev": true, "dependencies": { "brace-expansion": "^2.0.1" @@ -5541,9 +5549,9 @@ } }, "node_modules/immutable": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.5.tgz", - "integrity": "sha512-8eabxkth9gZatlwl5TBuJnCsoTADlL6ftEr7A4qgdaTsPyreilDSnUk57SO+jfKcNtxPa22U5KK6DSeAYhpBJw==", + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.6.tgz", + "integrity": "sha512-Ju0+lEMyzMVZarkTn/gqRpdqd5dOPaz1mCZ0SH3JV6iFw81PldE/PEB1hWVEA288HPt4WXW8O7AWxB10M+03QQ==", "dev": true }, "node_modules/import-fresh": { @@ -5581,6 +5589,7 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "dev": true, "dependencies": { "once": "^1.3.0", @@ -5948,9 +5957,9 @@ } }, "node_modules/jackspeak": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", - "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.1.2.tgz", + "integrity": "sha512-kWmLKn2tRtfYMF/BakihVVRzBKOxz4gJMiL2Rj91WnAB5TPZumSH99R/Yf1qE1u4uRimvCSJfm6hnxohXeEXjQ==", "dev": true, "dependencies": { "@isaacs/cliui": "^8.0.2" @@ -6292,9 +6301,9 @@ "dev": true }, "node_modules/lru-cache": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", - "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz", + "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==", "dev": true, "engines": { "node": "14 || >=16.14" @@ -6371,12 +6380,12 @@ "dev": true }, "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", + "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", "dev": true, "dependencies": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" }, "engines": { @@ -6459,9 +6468,9 @@ } }, "node_modules/minipass": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", - "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "dev": true, "engines": { "node": ">=16 || 14 >=14.17" @@ -6489,9 +6498,9 @@ } }, "node_modules/mocha": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.3.0.tgz", - "integrity": "sha512-uF2XJs+7xSLsrmIvn37i/wnc91nw7XjOQB8ccyx5aEgdnohr7n+rEiZP23WkCYHjilR6+EboEnbq/ZQDz4LSbg==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.4.0.tgz", + "integrity": "sha512-eqhGB8JKapEYcC4ytX/xrzKforgEc3j1pGlAXVy3eRwrtAy5/nIfT1SvgGzfN0XZZxeLq0aQWkOUAmqIJiv+bA==", "dev": true, "dependencies": { "ansi-colors": "4.1.1", @@ -6755,9 +6764,9 @@ } }, "node_modules/node-gyp-build": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.0.tgz", - "integrity": "sha512-u6fs2AEUljNho3EYTJNBfImO5QTo/J/1Etd+NVdCj7qWKUSN/bSLkZwhDv7I+w/MSC6qJ4cknepkAYykDdK8og==", + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.1.tgz", + "integrity": "sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw==", "dev": true, "bin": { "node-gyp-build": "bin.js", @@ -7040,16 +7049,16 @@ "dev": true }, "node_modules/path-scurry": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", - "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dev": true, "dependencies": { - "lru-cache": "^9.1.1 || ^10.0.0", + "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=16 || 14 >=14.18" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -7090,9 +7099,9 @@ } }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", "dev": true }, "node_modules/picomatch": { @@ -7126,9 +7135,9 @@ } }, "node_modules/pony-cause": { - "version": "2.1.10", - "resolved": "https://registry.npmjs.org/pony-cause/-/pony-cause-2.1.10.tgz", - "integrity": "sha512-3IKLNXclQgkU++2fSi93sQ6BznFuxSLB11HdvZQ6JW/spahf/P1pAHBQEahr20rs0htZW0UDkM1HmA+nZkXKsw==", + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/pony-cause/-/pony-cause-2.1.11.tgz", + "integrity": "sha512-M7LhCsdNbNgiLYiP4WjsfLUuFmCfnjdF6jKe2R9NKl4WFN+HZPGHJZ9lnLP7f9ZnKe3U9nuWD0szirmj+migUg==", "dev": true, "engines": { "node": ">=12.0.0" @@ -7759,13 +7768,10 @@ "dev": true }, "node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -7773,18 +7779,6 @@ "node": ">=10" } }, - "node_modules/semver/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/serialize-javascript": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", @@ -8046,9 +8040,9 @@ } }, "node_modules/solhint": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/solhint/-/solhint-4.5.2.tgz", - "integrity": "sha512-o7MNYS5QPgE6l+PTGOTAUtCzo0ZLnffQsv586hntSHBe2JbSDfkoxfhAOcjZjN4OesTgaX4UEEjCjH9y/4BP5w==", + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/solhint/-/solhint-4.5.4.tgz", + "integrity": "sha512-Cu1XiJXub2q1eCr9kkJ9VPv1sGcmj3V7Zb76B0CoezDOB9bu3DxKIFFH7ggCl9fWpEPD6xBmRLfZrYijkVmujQ==", "dev": true, "dependencies": { "@solidity-parser/parser": "^0.18.0", @@ -8242,9 +8236,9 @@ "dev": true }, "node_modules/solidity-coverage": { - "version": "0.8.11", - "resolved": "https://registry.npmjs.org/solidity-coverage/-/solidity-coverage-0.8.11.tgz", - "integrity": "sha512-yy0Yk+olovBbXn0Me8BWULmmv7A69ZKkP5aTOJGOO8u61Tu2zS989erfjtFlUjDnfWtxRAVkd8BsQD704yLWHw==", + "version": "0.8.12", + "resolved": "https://registry.npmjs.org/solidity-coverage/-/solidity-coverage-0.8.12.tgz", + "integrity": "sha512-8cOB1PtjnjFRqOgwFiD8DaUsYJtVJ6+YdXQtSZDrLGf8cdhhh8xzTtGzVTGeBf15kTv0v7lYPJlV/az7zLEPJw==", "dev": true, "dependencies": { "@ethersproject/abi": "^5.0.9", @@ -8257,7 +8251,7 @@ "global-modules": "^2.0.0", "globby": "^10.0.1", "jsonschema": "^1.2.4", - "lodash": "^4.17.15", + "lodash": "^4.17.21", "mocha": "^10.2.0", "node-emoji": "^1.10.0", "pify": "^4.0.1", @@ -8548,9 +8542,9 @@ } }, "node_modules/table": { - "version": "6.8.1", - "resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz", - "integrity": "sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==", + "version": "6.8.2", + "resolved": "https://registry.npmjs.org/table/-/table-6.8.2.tgz", + "integrity": "sha512-w2sfv80nrAh2VCbqR5AK27wswXhqcck2AhfnNW76beQXskGZ1V12GwS//yYVa3d3fcvAip2OUnbDAjW2k3v9fA==", "dev": true, "dependencies": { "ajv": "^8.0.1", @@ -8597,15 +8591,15 @@ } }, "node_modules/table/node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz", + "integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==", "dev": true, "dependencies": { - "fast-deep-equal": "^3.1.1", + "fast-deep-equal": "^3.1.3", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "uri-js": "^4.4.1" }, "funding": { "type": "github", @@ -9060,9 +9054,9 @@ } }, "node_modules/typescript": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz", - "integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==", + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -9110,9 +9104,9 @@ } }, "node_modules/undici": { - "version": "5.28.3", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.3.tgz", - "integrity": "sha512-3ItfzbrhDlINjaP0duwnNsKpDQk3acHI3gVJ1z4fmwMK31k5G9OVIAMLSIaP6w4FaGkaAkN6zaQO9LUvZ1t7VA==", + "version": "5.28.4", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.4.tgz", + "integrity": "sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==", "dev": true, "dependencies": { "@fastify/busboy": "^2.0.0" @@ -9572,12 +9566,6 @@ "node": ">=10" } }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", diff --git a/test/libs/data-structures/AvlTree.test.ts b/test/libs/data-structures/AvlTree.test.ts new file mode 100644 index 00000000..9fa065cf --- /dev/null +++ b/test/libs/data-structures/AvlTree.test.ts @@ -0,0 +1,638 @@ +import { ethers } from "hardhat"; +import { expect } from "chai"; +import { Reverter } from "@/test/helpers/reverter"; +import { AvlTreeMock } from "@ethers-v6"; +import { ZERO_ADDR } from "@/scripts/utils/constants"; +import { SignerWithAddress } from "@nomicfoundation/hardhat-ethers/signers"; +import { encodeBytes32String } from "ethers"; + +describe("AvlTree", () => { + const reverter = new Reverter(); + + let USER1: SignerWithAddress; + let USER2: SignerWithAddress; + let USER3: SignerWithAddress; + let USER4: SignerWithAddress; + let USER5: SignerWithAddress; + let USER6: SignerWithAddress; + + let avlTree: AvlTreeMock; + + before(async () => { + [USER1, USER2, USER3, USER4, USER5, USER6] = await ethers.getSigners(); + const AvlTreeMock = await ethers.getContractFactory("AvlTreeMock"); + + avlTree = await AvlTreeMock.deploy(); + + await reverter.snapshot(); + }); + + afterEach(reverter.revert); + + function uintToBytes32Array(uintArray: Array) { + let bytes32Array = []; + + for (let i = 0; i < uintArray.length; i++) { + bytes32Array.push(encodeBytes32String(uintArray[i].toString())); + } + + return bytes32Array; + } + + describe("Uint Tree", () => { + describe("insert", () => { + it("should insert uint values to the uint tree correctly", async () => { + await avlTree.setUintValueComparatorUint(); + + await avlTree.insertUintToUint(1, 4); + await avlTree.insertUintToUint(2, 12); + await avlTree.insertUintToUint(3, 1); + + expect(await avlTree.rootUint()).to.equal(1); + expect(await avlTree.treeSizeUint()).to.equal(3); + expect(await avlTree.inOrderTraversalUint()).to.deep.equal([3, 1, 2]); + expect(await avlTree.preOrderTraversalUint()).to.deep.equal([1, 3, 2]); + expect(await avlTree.postOrderTraversalUint()).to.deep.equal([3, 2, 1]); + + await avlTree.insertUintToUint(4, 200); + await avlTree.insertUintToUint(5, 10); + await avlTree.insertUintToUint(6, 5); + + expect(await avlTree.rootUint()).to.equal(5); + expect(await avlTree.treeSizeUint()).to.equal(6); + expect(await avlTree.inOrderTraversalUint()).to.deep.equal([3, 1, 6, 5, 2, 4]); + expect(await avlTree.preOrderTraversalUint()).to.deep.equal([5, 1, 3, 6, 2, 4]); + expect(await avlTree.postOrderTraversalUint()).to.deep.equal([3, 6, 1, 4, 2, 5]); + + await avlTree.insertUintToUint(7, 15); + + expect(await avlTree.rootUint()).to.equal(5); + expect(await avlTree.treeSizeUint()).to.equal(7); + expect(await avlTree.inOrderTraversalUint()).to.deep.equal([3, 1, 6, 5, 2, 7, 4]); + expect(await avlTree.preOrderTraversalUint()).to.deep.equal([5, 1, 3, 6, 7, 2, 4]); + expect(await avlTree.postOrderTraversalUint()).to.deep.equal([3, 6, 1, 2, 4, 7, 5]); + + await avlTree.insertUintToUint(8, 2); + await avlTree.insertUintToUint(9, 3); + + expect(await avlTree.rootUint()).to.equal(5); + expect(await avlTree.treeSizeUint()).to.equal(9); + expect(await avlTree.inOrderTraversalUint()).to.deep.equal([3, 8, 9, 1, 6, 5, 2, 7, 4]); + expect(await avlTree.preOrderTraversalUint()).to.deep.equal([5, 1, 8, 3, 9, 6, 7, 2, 4]); + expect(await avlTree.postOrderTraversalUint()).to.deep.equal([3, 9, 8, 6, 1, 2, 4, 7, 5]); + }); + + it("should insert address values to the uint tree correctly", async () => { + await avlTree.insertAddressToUint(2, USER2); + await avlTree.insertAddressToUint(6, USER1); + await avlTree.insertAddressToUint(4, USER2); + + expect(await avlTree.rootUint()).to.equal(4); + expect(await avlTree.treeSizeUint()).to.equal(3); + expect(await avlTree.inOrderTraversalUint()).to.deep.equal([2, 4, 6]); + expect(await avlTree.preOrderTraversalUint()).to.deep.equal([4, 2, 6]); + expect(await avlTree.postOrderTraversalUint()).to.deep.equal([2, 6, 4]); + + await avlTree.insertAddressToUint(1, USER2); + await avlTree.insertAddressToUint(13, USER4); + await avlTree.insertAddressToUint(7, USER1); + await avlTree.insertAddressToUint(5, USER3); + + expect(await avlTree.rootUint()).to.equal(4); + expect(await avlTree.treeSizeUint()).to.equal(7); + expect(await avlTree.inOrderTraversalUint()).to.deep.equal([1, 2, 4, 5, 6, 7, 13]); + expect(await avlTree.preOrderTraversalUint()).to.deep.equal([4, 2, 1, 7, 6, 5, 13]); + expect(await avlTree.postOrderTraversalUint()).to.deep.equal([1, 2, 5, 6, 13, 7, 4]); + + await avlTree.insertAddressToUint(9, USER4); + await avlTree.insertAddressToUint(8, USER4); + + expect(await avlTree.rootUint()).to.equal(4); + expect(await avlTree.treeSizeUint()).to.equal(9); + expect(await avlTree.inOrderTraversalUint()).to.deep.equal([1, 2, 4, 5, 6, 7, 8, 9, 13]); + expect(await avlTree.preOrderTraversalUint()).to.deep.equal([4, 2, 1, 7, 6, 5, 9, 8, 13]); + expect(await avlTree.postOrderTraversalUint()).to.deep.equal([1, 2, 5, 6, 8, 13, 9, 7, 4]); + }); + + it("should insert string values to the uint tree correctly", async () => { + await avlTree.setUintValueComparatorString(); + + await avlTree.insertStringToUint(1, "b"); + await avlTree.insertStringToUint(2, "f"); + await avlTree.insertStringToUint(3, "g"); + await avlTree.insertStringToUint(4, "a"); + await avlTree.insertStringToUint(5, "d"); + + expect(await avlTree.rootUint()).to.equal(2); + expect(await avlTree.treeSizeUint()).to.equal(5); + expect(await avlTree.inOrderTraversalUint()).to.deep.equal([4, 1, 5, 2, 3]); + expect(await avlTree.preOrderTraversalUint()).to.deep.equal([2, 1, 4, 5, 3]); + expect(await avlTree.postOrderTraversalUint()).to.deep.equal([4, 5, 1, 3, 2]); + + await avlTree.insertStringToUint(6, "e"); + await avlTree.insertStringToUint(7, "c"); + + expect(await avlTree.rootUint()).to.equal(5); + expect(await avlTree.treeSizeUint()).to.equal(7); + expect(await avlTree.inOrderTraversalUint()).to.deep.equal([4, 1, 7, 5, 6, 2, 3]); + expect(await avlTree.preOrderTraversalUint()).to.deep.equal([5, 1, 4, 7, 2, 6, 3]); + expect(await avlTree.postOrderTraversalUint()).to.deep.equal([4, 7, 1, 6, 3, 2, 5]); + }); + + it("should insert struct values to the uint tree correctly", async () => { + await avlTree.setUintValueComparatorStruct(); + + await avlTree.insertStructToUint(1, await avlTree.valueToBytesStruct(20)); + await avlTree.insertStructToUint(2, await avlTree.valueToBytesStruct(1)); + await avlTree.insertStructToUint(3, await avlTree.valueToBytesStruct(18)); + + expect(await avlTree.rootUint()).to.equal(3); + expect(await avlTree.treeSizeUint()).to.equal(3); + expect(await avlTree.inOrderTraversalUint()).to.deep.equal([2, 3, 1]); + expect(await avlTree.preOrderTraversalUint()).to.deep.equal([3, 2, 1]); + expect(await avlTree.postOrderTraversalUint()).to.deep.equal([2, 1, 3]); + + await avlTree.insertStructToUint(4, await avlTree.valueToBytesStruct(17)); + await avlTree.insertStructToUint(5, await avlTree.valueToBytesStruct(16)); + + expect(await avlTree.rootUint()).to.equal(3); + expect(await avlTree.treeSizeUint()).to.equal(5); + expect(await avlTree.inOrderTraversalUint()).to.deep.equal([2, 5, 4, 3, 1]); + expect(await avlTree.preOrderTraversalUint()).to.deep.equal([3, 5, 2, 4, 1]); + expect(await avlTree.postOrderTraversalUint()).to.deep.equal([2, 4, 5, 1, 3]); + + await avlTree.insertStructToUint(6, await avlTree.valueToBytesStruct(2)); + + expect(await avlTree.rootUint()).to.equal(5); + expect(await avlTree.treeSizeUint()).to.equal(6); + expect(await avlTree.inOrderTraversalUint()).to.deep.equal([2, 6, 5, 4, 3, 1]); + expect(await avlTree.preOrderTraversalUint()).to.deep.equal([5, 2, 6, 3, 4, 1]); + expect(await avlTree.postOrderTraversalUint()).to.deep.equal([6, 2, 4, 1, 3, 5]); + }); + + it("should not allow to insert 0 as key to the uint tree", async () => { + await expect(avlTree.insertStringToUint(0, "test")).to.be.revertedWith("AvlTree: key is not allowed to be 0"); + }); + + it("should not allow to insert node with duplicate key to the uint tree", async () => { + await avlTree.insertUintToUint(2, 10); + await expect(avlTree.insertUintToUint(2, 4)).to.be.revertedWith("AvlTree: the node already exists"); + }); + }); + + describe("remove", () => { + it("should remove a node in the uint tree correctly", async () => { + await avlTree.insertUintToUint(2, 4); + await avlTree.insertUintToUint(3, 8); + await avlTree.insertUintToUint(6, 10); + await avlTree.insertUintToUint(1, 2); + await avlTree.insertUintToUint(4, 10); + await avlTree.insertUintToUint(7, 10); + await avlTree.insertUintToUint(10, 10); + + expect(await avlTree.rootUint()).to.equal(3); + expect(await avlTree.treeSizeUint()).to.equal(7); + + await avlTree.removeUint(3); + + expect(await avlTree.rootUint()).to.equal(4); + expect(await avlTree.treeSizeUint()).to.equal(6); + expect(await avlTree.inOrderTraversalUint()).to.deep.equal([1, 2, 4, 6, 7, 10]); + expect(await avlTree.preOrderTraversalUint()).to.deep.equal([4, 2, 1, 7, 6, 10]); + expect(await avlTree.postOrderTraversalUint()).to.deep.equal([1, 2, 6, 10, 7, 4]); + }); + + it("should remove multiple nodes in the uint tree correctly", async () => { + await avlTree.insertUintToUint(1, 2); + await avlTree.insertUintToUint(4, 20); + await avlTree.insertUintToUint(3, 4); + await avlTree.insertUintToUint(7, 1); + await avlTree.insertUintToUint(8, 10); + await avlTree.insertUintToUint(9, 1); + await avlTree.insertUintToUint(5, 12); + await avlTree.insertUintToUint(6, 11); + + await avlTree.removeUint(8); + await avlTree.removeUint(3); + await avlTree.removeUint(5); + + expect(await avlTree.rootUint()).to.equal(6); + expect(await avlTree.treeSizeUint()).to.equal(5); + expect(await avlTree.inOrderTraversalUint()).to.deep.equal([1, 4, 6, 7, 9]); + expect(await avlTree.preOrderTraversalUint()).to.deep.equal([6, 4, 1, 7, 9]); + expect(await avlTree.postOrderTraversalUint()).to.deep.equal([1, 4, 9, 7, 6]); + }); + + it("should handle removing all the nodes in the uint tree correctly", async () => { + await avlTree.insertUintToUint(2, 1); + await avlTree.insertUintToUint(4, 10); + await avlTree.insertUintToUint(3, 2); + await avlTree.removeUint(4); + await avlTree.removeUint(2); + await avlTree.insertUintToUint(2, 2); + await avlTree.insertUintToUint(1, 2); + await avlTree.removeUint(2); + await avlTree.removeUint(1); + await avlTree.removeUint(3); + + expect(await avlTree.rootUint()).to.be.equal(0); + expect(await avlTree.treeSizeUint()).to.be.equal(0); + }); + + it("should not allow to remove a node that doesn't exist in the uint tree", async () => { + await avlTree.insertUintToUint(1, 10); + + await expect(avlTree.removeUint(2)).to.be.revertedWith("AvlTree: the node doesn't exist"); + await expect(avlTree.removeUint(0)).to.be.revertedWith("AvlTree: key is not allowed to be 0"); + }); + + it("should not allow to remove a node twice in the uint tree", async () => { + await avlTree.insertUintToUint(1, 10); + await avlTree.insertUintToUint(2, 20); + + await avlTree.removeUint(2); + + await expect(avlTree.removeUint(2)).to.be.revertedWith("AvlTree: the node doesn't exist"); + }); + + it("should not allow to remove nodes in the empty uint tree", async () => { + await expect(avlTree.removeUint(1)).to.be.revertedWith("AvlTree: the node doesn't exist"); + }); + }); + + describe("setComparator", () => { + it("should set a comparator for the uint tree correctly", async () => { + await avlTree.setUintDescComparator(); + + await avlTree.insertUintToUint(2, 3); + await avlTree.insertUintToUint(4, 10); + await avlTree.insertUintToUint(1, 4); + await avlTree.insertUintToUint(6, 4); + await avlTree.insertUintToUint(3, 200); + await avlTree.insertUintToUint(5, 1); + + expect(await avlTree.rootUint()).to.equal(4); + expect(await avlTree.treeSizeUint()).to.equal(6); + expect(await avlTree.inOrderTraversalUint()).to.deep.equal([6, 5, 4, 3, 2, 1]); + expect(await avlTree.preOrderTraversalUint()).to.deep.equal([4, 6, 5, 2, 3, 1]); + expect(await avlTree.postOrderTraversalUint()).to.deep.equal([5, 6, 3, 1, 2, 4]); + }); + + it("should not allow to set comparator for the uint tree if the tree is not empty", async () => { + await avlTree.insertUintToUint(1, 10); + + await expect(avlTree.setUintDescComparator()).to.be.revertedWith("AvlTree: the tree must be empty"); + }); + }); + + describe("getters", () => { + it("should handle searching for the existing key in the uint tree correctly", async () => { + await avlTree.insertUintToUint(2, 20); + await avlTree.insertUintToUint(1, 10); + + expect(await avlTree.searchUint(2)).to.be.true; + }); + + it("should handle searching for the non-existing key in the uint tree correctly", async () => { + await avlTree.insertUintToUint(2, 1); + + expect(await avlTree.searchUint(3)).to.be.false; + }); + + it("should handle searching for the zero key in the uint tree correctly", async () => { + await avlTree.insertUintToUint(2, 1); + + expect(await avlTree.searchUint(0)).to.be.false; + }); + + it("should get value for the existing node in the uint tree correctly", async () => { + await avlTree.insertUintToUint(1, 4); + await avlTree.insertUintToUint(2, 2); + + await avlTree.insertAddressToUint(3, USER1); + await avlTree.insertAddressToUint(4, USER2); + + await avlTree.insertStringToUint(5, "test1"); + await avlTree.insertStringToUint(6, "test2"); + + expect(await avlTree.getUintValueUint(1)).to.equal(4); + expect(await avlTree.getUintValueUint(2)).to.equal(2); + + expect(await avlTree.getAddressValueUint(3)).to.equal(USER1); + expect(await avlTree.getAddressValueUint(4)).to.equal(USER2); + + expect(await avlTree.getStringValueUint(5)).to.equal("test1"); + expect(await avlTree.getStringValueUint(6)).to.equal("test2"); + }); + + it("should handle getting value for the non-existing node in the uint tree correctly", async () => { + await expect(avlTree.getUintValueUint(1)).to.be.revertedWith("AvlTree: node with such key doesn't exist"); + + await avlTree.insertUintToUint(1, 30); + await expect(avlTree.getUintValueUint(2)).to.be.revertedWith("AvlTree: node with such key doesn't exist"); + await expect(avlTree.getUintValueUint(0)).to.be.revertedWith("AvlTree: node with such key doesn't exist"); + }); + + it("should get minimum node in the uint tree correctly", async () => { + await avlTree.setUintValueComparatorUint(); + + await avlTree.insertUintToUint(4, 10); + await avlTree.insertUintToUint(5, 4); + await avlTree.insertUintToUint(1, 200); + await avlTree.insertUintToUint(2, 6); + + expect(await avlTree.getMinUint()).to.be.equal(5); + }); + + it("should get maximum node in the uint tree correctly", async () => { + await avlTree.insertUintToUint(4, 100); + await avlTree.insertUintToUint(5, 4); + await avlTree.insertUintToUint(8, 80); + await avlTree.insertUintToUint(1, 6); + + expect(await avlTree.getMaxUint()).to.be.equal(8); + }); + + it("should get the root key in the uint tree correctly", async () => { + expect(await avlTree.rootUint()).to.be.equal(0); + + await avlTree.insertUintToUint(1, 10); + await avlTree.insertUintToUint(100, 10); + await avlTree.insertUintToUint(22, 4); + await avlTree.insertUintToUint(7, 90); + await avlTree.insertUintToUint(2, 1); + await avlTree.insertUintToUint(3, 1); + + expect(await avlTree.rootUint()).to.be.equal(7); + }); + + it("should get the treeSize correctly for the uint tree", async () => { + expect(await avlTree.treeSizeUint()).to.be.equal(0); + + await avlTree.insertUintToUint(1, 10); + await avlTree.insertUintToUint(2, 10); + await avlTree.removeUint(1); + await avlTree.insertUintToUint(3, 4); + await avlTree.insertUintToUint(7, 90); + + expect(await avlTree.treeSizeUint()).to.be.equal(3); + }); + + it("should check if custom comparator is set for the uint tree correctly", async () => { + expect(await avlTree.isCustomComparatorSetUint()).to.be.false; + + await avlTree.setUintValueComparatorUint(); + + expect(await avlTree.isCustomComparatorSetUint()).to.be.true; + }); + }); + + it("should handle complex operations on the uint tree", async () => { + await avlTree.insertUintToUint(2, 10); + await avlTree.insertUintToUint(3, 1); + await avlTree.insertUintToUint(10, 18); + + await avlTree.removeUint(3); + + await avlTree.insertUintToUint(4, 20); + await avlTree.insertUintToUint(5, 10); + + await avlTree.removeUint(2); + await avlTree.removeUint(10); + await avlTree.removeUint(4); + + await avlTree.insertUintToUint(4, 7); + await avlTree.insertUintToUint(1, 20); + await avlTree.insertUintToUint(2, 10); + await avlTree.insertUintToUint(20, 20); + await avlTree.insertUintToUint(111, 10); + + await avlTree.removeUint(20); + + await avlTree.insertUintToUint(16, 20); + await avlTree.insertUintToUint(17, 100); + await avlTree.insertUintToUint(18, 20); + await avlTree.insertUintToUint(19, 250); + await avlTree.insertUintToUint(20, 4); + + expect(await avlTree.rootUint()).to.equal(4); + expect(await avlTree.treeSizeUint()).to.equal(10); + expect(await avlTree.inOrderTraversalUint()).to.deep.equal([1, 2, 4, 5, 16, 17, 18, 19, 20, 111]); + expect(await avlTree.preOrderTraversalUint()).to.deep.equal([4, 1, 2, 18, 16, 5, 17, 20, 19, 111]); + expect(await avlTree.postOrderTraversalUint()).to.deep.equal([2, 1, 5, 17, 16, 19, 111, 20, 18, 4]); + }); + }); + + describe("Bytes32 Tree", () => { + it("should insert nodes to the bytes32 tree correctly", async () => { + await avlTree.setBytes32DescComparator(); + + await avlTree.insertUintToBytes32(encodeBytes32String("2"), 20); + await avlTree.insertUintToBytes32(encodeBytes32String("4"), 22); + await avlTree.insertUintToBytes32(encodeBytes32String("1"), 12); + await avlTree.insertUintToBytes32(encodeBytes32String("6"), 2); + await avlTree.insertUintToBytes32(encodeBytes32String("3"), 112); + await avlTree.insertUintToBytes32(encodeBytes32String("5"), 2); + + expect(await avlTree.rootBytes32()).to.equal(encodeBytes32String("4")); + expect(await avlTree.getUintValueBytes32(encodeBytes32String("4"))).to.be.equal(22); + + expect(await avlTree.getMinBytes32()).to.equal(encodeBytes32String("6")); + expect(await avlTree.getUintValueBytes32(encodeBytes32String("6"))).to.be.equal(2); + + expect(await avlTree.getMaxBytes32()).to.equal(encodeBytes32String("1")); + expect(await avlTree.getUintValueBytes32(encodeBytes32String("1"))).to.be.equal(12); + + expect(await avlTree.treeSizeBytes32()).to.equal(6); + expect(await avlTree.inOrderTraversalBytes32()).to.deep.equal(uintToBytes32Array([6, 5, 4, 3, 2, 1])); + expect(await avlTree.preOrderTraversalBytes32()).to.deep.equal(uintToBytes32Array([4, 6, 5, 2, 3, 1])); + expect(await avlTree.postOrderTraversalBytes32()).to.deep.equal(uintToBytes32Array([5, 6, 3, 1, 2, 4])); + }); + + it("should remove nodes in the bytes32 tree correctly", async () => { + await avlTree.insertUintToBytes32(encodeBytes32String("2"), 2); + await avlTree.insertUintToBytes32(encodeBytes32String("1"), 1); + await avlTree.insertUintToBytes32(encodeBytes32String("5"), 5); + await avlTree.insertUintToBytes32(encodeBytes32String("4"), 6); + + await avlTree.removeBytes32(encodeBytes32String("2")); + + expect(await avlTree.rootBytes32()).to.equal(encodeBytes32String("4")); + + expect(await avlTree.treeSizeBytes32()).to.equal(3); + expect(await avlTree.searchBytes32(encodeBytes32String("2"))).to.be.false; + expect(await avlTree.inOrderTraversalBytes32()).to.deep.equal(uintToBytes32Array([1, 4, 5])); + expect(await avlTree.preOrderTraversalBytes32()).to.deep.equal(uintToBytes32Array([4, 1, 5])); + expect(await avlTree.postOrderTraversalBytes32()).to.deep.equal(uintToBytes32Array([1, 5, 4])); + + await avlTree.insertUintToBytes32(encodeBytes32String("3"), 3); + await avlTree.insertUintToBytes32(encodeBytes32String("6"), 4); + await avlTree.insertUintToBytes32(encodeBytes32String("2"), 2); + + await avlTree.removeBytes32(encodeBytes32String("1")); + await avlTree.removeBytes32(encodeBytes32String("3")); + + expect(await avlTree.rootBytes32()).to.equal(encodeBytes32String("4")); + expect(await avlTree.treeSizeBytes32()).to.equal(4); + + expect(await avlTree.searchBytes32(encodeBytes32String("2"))).to.be.true; + expect(await avlTree.searchBytes32(encodeBytes32String("1"))).to.be.false; + expect(await avlTree.searchBytes32(encodeBytes32String("3"))).to.be.false; + + expect(await avlTree.inOrderTraversalBytes32()).to.deep.equal(uintToBytes32Array([2, 4, 5, 6])); + expect(await avlTree.preOrderTraversalBytes32()).to.deep.equal(uintToBytes32Array([4, 2, 5, 6])); + expect(await avlTree.postOrderTraversalBytes32()).to.deep.equal(uintToBytes32Array([2, 6, 5, 4])); + + await avlTree.removeBytes32(encodeBytes32String("2")); + await avlTree.removeBytes32(encodeBytes32String("5")); + await avlTree.removeBytes32(encodeBytes32String("6")); + await avlTree.removeBytes32(encodeBytes32String("4")); + + expect(await avlTree.rootBytes32()).to.equal(encodeBytes32String("")); + expect(await avlTree.treeSizeBytes32()).to.equal(0); + }); + + it("should handle searching in the bytes32 tree correctly", async () => { + expect(await avlTree.searchBytes32(encodeBytes32String("1"))).to.be.false; + + await avlTree.insertUintToBytes32(encodeBytes32String("1"), 18); + + expect(await avlTree.searchBytes32(encodeBytes32String("1"))).to.be.true; + expect(await avlTree.searchBytes32(encodeBytes32String("2"))).to.be.false; + expect(await avlTree.searchBytes32(encodeBytes32String("0"))).to.be.false; + }); + + it("should check if custom comparator is set for the bytes32 tree correctly", async () => { + expect(await avlTree.isCustomComparatorSetBytes32()).to.be.false; + + await avlTree.setBytes32DescComparator(); + + expect(await avlTree.isCustomComparatorSetBytes32()).to.be.true; + }); + }); + + describe("Address Tree", () => { + it("should insert nodes to the address tree correctly", async () => { + await avlTree.setUintValueComparatorAddress(); + + await avlTree.insertUintToAddress(USER1, 10); + await avlTree.insertUintToAddress(USER2, 22); + await avlTree.insertUintToAddress(USER3, 15); + await avlTree.insertUintToAddress(USER4, 6); + await avlTree.insertUintToAddress(USER5, 16); + await avlTree.insertUintToAddress(USER6, 17); + + expect(await avlTree.rootAddress()).to.equal(USER3); + expect(await avlTree.getUintValueAddress(USER3)).to.be.equal(15); + + expect(await avlTree.getMinAddress()).to.equal(USER4); + expect(await avlTree.getUintValueAddress(USER4)).to.be.equal(6); + + expect(await avlTree.getMaxAddress()).to.equal(USER2); + expect(await avlTree.getUintValueAddress(USER2)).to.be.equal(22); + + expect(await avlTree.treeSizeAddress()).to.equal(6); + expect(await avlTree.inOrderTraversalAddress()).to.deep.equal([ + USER4.address, + USER1.address, + USER3.address, + USER5.address, + USER6.address, + USER2.address, + ]); + expect(await avlTree.preOrderTraversalAddress()).to.deep.equal([ + USER3.address, + USER1.address, + USER4.address, + USER6.address, + USER5.address, + USER2.address, + ]); + expect(await avlTree.postOrderTraversalAddress()).to.deep.equal([ + USER4.address, + USER1.address, + USER5.address, + USER2.address, + USER6.address, + USER3.address, + ]); + }); + + it("should remove nodes in the address tree correctly", async () => { + await avlTree.setUintValueComparatorAddress(); + + await avlTree.insertUintToAddress(USER1, 2); + await avlTree.insertUintToAddress(USER2, 1); + await avlTree.insertUintToAddress(USER3, 5); + await avlTree.insertUintToAddress(USER4, 6); + + await avlTree.removeAddress(USER1); + + expect(await avlTree.rootAddress()).to.equal(USER3); + expect(await avlTree.treeSizeAddress()).to.equal(3); + expect(await avlTree.searchAddress(USER1)).to.be.false; + expect(await avlTree.inOrderTraversalAddress()).to.deep.equal([USER2.address, USER3.address, USER4.address]); + expect(await avlTree.preOrderTraversalAddress()).to.deep.equal([USER3.address, USER2.address, USER4.address]); + expect(await avlTree.postOrderTraversalAddress()).to.deep.equal([USER2.address, USER4.address, USER3.address]); + + await avlTree.insertUintToAddress(USER5, 3); + await avlTree.insertUintToAddress(USER6, 4); + await avlTree.insertUintToAddress(USER1, 2); + + await avlTree.removeAddress(USER2); + await avlTree.removeAddress(USER5); + + expect(await avlTree.rootAddress()).to.equal(USER6); + expect(await avlTree.treeSizeAddress()).to.equal(4); + + expect(await avlTree.searchAddress(USER1)).to.be.true; + expect(await avlTree.searchAddress(USER2)).to.be.false; + expect(await avlTree.searchAddress(USER5)).to.be.false; + + expect(await avlTree.inOrderTraversalAddress()).to.deep.equal([ + USER1.address, + USER6.address, + USER3.address, + USER4.address, + ]); + expect(await avlTree.preOrderTraversalAddress()).to.deep.equal([ + USER6.address, + USER1.address, + USER3.address, + USER4.address, + ]); + expect(await avlTree.postOrderTraversalAddress()).to.deep.equal([ + USER1.address, + USER4.address, + USER3.address, + USER6.address, + ]); + + await avlTree.removeAddress(USER1); + await avlTree.removeAddress(USER3); + await avlTree.removeAddress(USER4); + await avlTree.removeAddress(USER6); + + expect(await avlTree.rootAddress()).to.equal(ZERO_ADDR); + expect(await avlTree.treeSizeAddress()).to.equal(0); + }); + + it("should handle searching in the address tree correctly", async () => { + expect(await avlTree.searchAddress(USER1)).to.be.false; + + await avlTree.insertUintToAddress(USER1, 2); + + expect(await avlTree.searchAddress(USER1)).to.be.true; + expect(await avlTree.searchAddress(USER5)).to.be.false; + expect(await avlTree.searchAddress(ZERO_ADDR)).to.be.false; + }); + + it("should check if custom comparator is set for the address tree correctly", async () => { + expect(await avlTree.isCustomComparatorSetAddress()).to.be.false; + + await avlTree.setAddressDescComparator(); + + expect(await avlTree.isCustomComparatorSetAddress()).to.be.true; + }); + }); +}); From 33afd895e45d62a1aeed2ba6764493950a1923d1 Mon Sep 17 00:00:00 2001 From: mllwchrry Date: Wed, 5 Jun 2024 18:58:35 +0300 Subject: [PATCH 04/11] added Traversal library --- contracts/libs/data-structures/AvlTree.sol | 684 +++++++++--------- .../mock/libs/data-structures/AvlTreeMock.sol | 356 +++++---- test/libs/data-structures/AvlTree.test.ts | 504 ++++++------- 3 files changed, 754 insertions(+), 790 deletions(-) diff --git a/contracts/libs/data-structures/AvlTree.sol b/contracts/libs/data-structures/AvlTree.sol index 7562e6dd..c17a7bab 100644 --- a/contracts/libs/data-structures/AvlTree.sol +++ b/contracts/libs/data-structures/AvlTree.sol @@ -2,8 +2,107 @@ pragma solidity ^0.8.4; import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; + import {TypeCaster} from "../utils/TypeCaster.sol"; +library Traversal { + struct Iterator { + uint256 treeMappingSlot; + uint64 currentNode; + } + + function hasNext(Iterator memory iterator_) internal view returns (bool) { + AvlTree.Node memory node_ = _getNode(iterator_.treeMappingSlot, iterator_.currentNode); + + if (node_.right != 0) { + return true; + } + + uint64 currentNodeIndex_ = iterator_.currentNode; + + while (currentNodeIndex_ != 0) { + AvlTree.Node memory parent_ = _getNode(iterator_.treeMappingSlot, node_.parent); + + if (currentNodeIndex_ == parent_.left) { + return true; + } + + currentNodeIndex_ = node_.parent; + node_ = parent_; + } + + return false; + } + + function next(Iterator memory iterator_) internal view returns (bytes32, bytes32) { + uint64 currentNodeIndex_ = iterator_.currentNode; + + require(currentNodeIndex_ != 0, "Traversal: No more nodes"); + + AvlTree.Node memory node_ = _getNode(iterator_.treeMappingSlot, currentNodeIndex_); + + if (node_.right != 0) { + currentNodeIndex_ = node_.right; + + AvlTree.Node memory childNode_ = _getNode( + iterator_.treeMappingSlot, + currentNodeIndex_ + ); + + while (childNode_.left != 0) { + currentNodeIndex_ = childNode_.left; + childNode_ = _getNode(iterator_.treeMappingSlot, currentNodeIndex_); + } + } else { + uint64 parentIndex_ = node_.parent; + + AvlTree.Node memory parentNode_ = _getNode(iterator_.treeMappingSlot, parentIndex_); + + while (parentIndex_ != 0 && currentNodeIndex_ == parentNode_.right) { + currentNodeIndex_ = parentIndex_; + + parentIndex_ = parentNode_.parent; + parentNode_ = _getNode(iterator_.treeMappingSlot, parentIndex_); + } + + currentNodeIndex_ = parentIndex_; + } + + iterator_.currentNode = currentNodeIndex_; + + return value(iterator_); + } + + function value(Iterator memory iterator_) internal view returns (bytes32, bytes32) { + AvlTree.Node memory node_ = _getNode(iterator_.treeMappingSlot, iterator_.currentNode); + + return (node_.key, node_.value); + } + + function _getNode( + uint256 slot_, + uint64 index_ + ) private view returns (AvlTree.Node memory node_) { + bytes32 baseSlot_ = keccak256(abi.encode(index_, slot_)); + + assembly { + let valueSlot_ := add(baseSlot_, 1) + let packedSlot_ := add(baseSlot_, 2) + + let packedData_ := sload(packedSlot_) + + mstore(node_, sload(baseSlot_)) + mstore(add(node_, 0x20), sload(valueSlot_)) + mstore(add(node_, 0x40), and(packedData_, 0xFFFFFFFFFFFFFFFF)) + mstore(add(node_, 0x60), and(shr(64, packedData_), 0xFFFFFFFFFFFFFFFF)) + mstore(add(node_, 0x80), and(shr(128, packedData_), 0xFFFFFFFFFFFFFFFF)) + mstore(add(node_, 0xa0), and(shr(192, packedData_), 0xFFFFFFFFFFFFFFFF)) + } + + return node_; + } +} + /** * @notice AVL Tree module * @@ -66,7 +165,7 @@ library AvlTree { */ function setComparator( UintAVL storage tree, - function(bytes32, bytes32, bytes memory, bytes memory) view returns (int8) comparator_ + function(bytes32, bytes32) view returns (int8) comparator_ ) internal { _setComparator(tree._tree, comparator_); } @@ -79,7 +178,7 @@ library AvlTree { * @param key_ the key to insert. * @param value_ the value to insert. */ - function insert(UintAVL storage tree, uint256 key_, bytes memory value_) internal { + function insert(UintAVL storage tree, uint256 key_, bytes32 value_) internal { _insert(tree._tree, bytes32(key_), value_); } @@ -102,8 +201,9 @@ library AvlTree { * @param key_ the key of the node to search for. * @return True if the node exists, false otherwise. */ - function search(UintAVL storage tree, uint256 key_) internal view returns (bool) { - return _search(tree._tree, bytes32(key_)); + function search(UintAVL storage tree, uint256 key_) internal view returns (uint64) { + return + _search(tree._tree.tree, tree._tree.root, bytes32(key_), _getComparator(tree._tree)); } /** @@ -114,43 +214,8 @@ library AvlTree { * @param key_ the key to get the value for. * @return The value associated with the key. */ - function getValue(UintAVL storage tree, uint256 key_) internal view returns (bytes storage) { - require(_search(tree._tree, bytes32(key_)), "AvlTree: node with such key doesn't exist"); - - return tree._tree.tree[bytes32(key_)].value; - } - - /** - * @notice The function to retrieve the minimum key in the uint256 tree. - * Complexity is O(log(n)), where n is the number of elements in the tree. - * - * @param tree self. - * @return The minimum key in the tree. - */ - function getMin(UintAVL storage tree) internal view returns (uint256) { - return uint256(_getMin(tree._tree.tree, tree._tree.root)); - } - - /** - * @notice The function to retrieve the maximum key in the uint256 tree. - * Complexity is O(log(n)), where n is the number of elements in the tree. - * - * @param tree self. - * @return The maximum key in the tree. - */ - function getMax(UintAVL storage tree) internal view returns (uint256) { - return uint256(_getMax(tree._tree.tree, tree._tree.root)); - } - - /** - * @notice The function to return the key of the root element of the uint256 tree. - * Complexity is O(1). - * - * @param tree self. - * @return The key of the root element of the uint256 tree. - */ - function root(UintAVL storage tree) internal view returns (uint256) { - return uint256(tree._tree.root); + function getValue(UintAVL storage tree, uint256 key_) internal view returns (bool, bytes32) { + return _getValue(tree._tree, bytes32(key_)); } /** @@ -158,35 +223,18 @@ library AvlTree { * @param tree self. * @return The size of the tree. */ - function treeSize(UintAVL storage tree) internal view returns (uint256) { - return tree._tree.treeSize; - } - - /** - * @notice The function to perform an in-order traversal of the uint256 tree. - * @param tree self. - * @return An array of keys in in-order traversal. - */ - function inOrderTraversal(UintAVL storage tree) internal view returns (uint256[] memory) { - return _getTraversal(tree._tree, _inOrderTraversal).asUint256Array(); + function treeSize(UintAVL storage tree) internal view returns (uint64) { + return uint64(_treeSize(tree._tree)); } - /** - * @notice The function to perform an pre-order traversal of the uint256 tree. - * @param tree self. - * @return An array of keys in pre-order traversal. - */ - function preOrderTraversal(UintAVL storage tree) internal view returns (uint256[] memory) { - return _getTraversal(tree._tree, _preOrderTraversal).asUint256Array(); + function beginTraversal( + UintAVL storage tree + ) internal view returns (Traversal.Iterator memory) { + return _beginTraversal(tree._tree); } - /** - * @notice The function to perform an post-order traversal of the uint256 tree. - * @param tree self. - * @return An array of keys in post-order traversal. - */ - function postOrderTraversal(UintAVL storage tree) internal view returns (uint256[] memory) { - return _getTraversal(tree._tree, _postOrderTraversal).asUint256Array(); + function endTraversal(UintAVL storage tree) internal view returns (Traversal.Iterator memory) { + return _endTraversal(tree._tree); } /** @@ -215,7 +263,7 @@ library AvlTree { */ function setComparator( Bytes32AVL storage tree, - function(bytes32, bytes32, bytes memory, bytes memory) view returns (int8) comparator_ + function(bytes32, bytes32) view returns (int8) comparator_ ) internal { _setComparator(tree._tree, comparator_); } @@ -228,7 +276,7 @@ library AvlTree { * @param key_ the key to insert. * @param value_ the value to insert. */ - function insert(Bytes32AVL storage tree, bytes32 key_, bytes memory value_) internal { + function insert(Bytes32AVL storage tree, bytes32 key_, bytes32 value_) internal { _insert(tree._tree, key_, value_); } @@ -251,8 +299,8 @@ library AvlTree { * @param key_ the key of the node to search for. * @return True if the node exists, false otherwise. */ - function search(Bytes32AVL storage tree, bytes32 key_) internal view returns (bool) { - return _search(tree._tree, key_); + function search(Bytes32AVL storage tree, bytes32 key_) internal view returns (uint64) { + return _search(tree._tree.tree, tree._tree.root, key_, _getComparator(tree._tree)); } /** @@ -266,42 +314,8 @@ library AvlTree { function getValue( Bytes32AVL storage tree, bytes32 key_ - ) internal view returns (bytes storage) { - require(_search(tree._tree, key_), "AvlTree: node with such key doesn't exist"); - return tree._tree.tree[key_].value; - } - - /** - * @notice The function to retrieve the minimum key in the bytes32 tree. - * Complexity is O(log(n)), where n is the number of elements in the tree. - * - * @param tree self. - * @return The minimum key in the tree. - */ - function getMin(Bytes32AVL storage tree) internal view returns (bytes32) { - return _getMin(tree._tree.tree, tree._tree.root); - } - - /** - * @notice The function to retrieve the maximum key in the bytes32 tree. - * Complexity is O(log(n)), where n is the number of elements in the tree. - * - * @param tree self. - * @return The maximum key in the tree. - */ - function getMax(Bytes32AVL storage tree) internal view returns (bytes32) { - return _getMax(tree._tree.tree, tree._tree.root); - } - - /** - * @notice The function to return the key of the root element of the bytes32 tree. - * Complexity is O(1). - * - * @param tree self. - * @return The key of the root element of the uint256 tree. - */ - function root(Bytes32AVL storage tree) internal view returns (bytes32) { - return tree._tree.root; + ) internal view returns (bool, bytes32) { + return _getValue(tree._tree, key_); } /** @@ -309,35 +323,20 @@ library AvlTree { * @param tree self. * @return The size of the tree. */ - function treeSize(Bytes32AVL storage tree) internal view returns (uint256) { - return tree._tree.treeSize; - } - - /** - * @notice The function to perform an in-order traversal of the bytes32 tree. - * @param tree self. - * @return An array of keys in in-order traversal. - */ - function inOrderTraversal(Bytes32AVL storage tree) internal view returns (bytes32[] memory) { - return _getTraversal(tree._tree, _inOrderTraversal); + function treeSize(Bytes32AVL storage tree) internal view returns (uint64) { + return uint64(_treeSize(tree._tree)); } - /** - * @notice The function to perform an pre-order traversal of the bytes32 tree. - * @param tree self. - * @return An array of keys in pre-order traversal. - */ - function preOrderTraversal(Bytes32AVL storage tree) internal view returns (bytes32[] memory) { - return _getTraversal(tree._tree, _preOrderTraversal); + function beginTraversal( + Bytes32AVL storage tree + ) internal view returns (Traversal.Iterator memory) { + return _beginTraversal(tree._tree); } - /** - * @notice The function to perform an post-order traversal of the bytes32 tree. - * @param tree self. - * @return An array of keys in post-order traversal. - */ - function postOrderTraversal(Bytes32AVL storage tree) internal view returns (bytes32[] memory) { - return _getTraversal(tree._tree, _postOrderTraversal); + function endTraversal( + Bytes32AVL storage tree + ) internal view returns (Traversal.Iterator memory) { + return _endTraversal(tree._tree); } /** @@ -366,7 +365,7 @@ library AvlTree { */ function setComparator( AddressAVL storage tree, - function(bytes32, bytes32, bytes memory, bytes memory) view returns (int8) comparator_ + function(bytes32, bytes32) view returns (int8) comparator_ ) internal { _setComparator(tree._tree, comparator_); } @@ -379,8 +378,8 @@ library AvlTree { * @param key_ The key to insert. * @param value_ The value to insert. */ - function insert(AddressAVL storage tree, address key_, bytes memory value_) internal { - _insert(tree._tree, _asBytes32(key_), value_); + function insert(AddressAVL storage tree, address key_, bytes32 value_) internal { + _insert(tree._tree, bytes32(uint256(uint160(key_))), value_); } /** @@ -391,7 +390,7 @@ library AvlTree { * @param key_ the key of the node to remove. */ function remove(AddressAVL storage tree, address key_) internal { - _remove(tree._tree, _asBytes32(key_)); + _remove(tree._tree, bytes32(uint256(uint160(key_)))); } /** @@ -402,8 +401,14 @@ library AvlTree { * @param key_ the key of the node to search for. * @return True if the node exists, false otherwise. */ - function search(AddressAVL storage tree, address key_) internal view returns (bool) { - return _search(tree._tree, _asBytes32(key_)); + function search(AddressAVL storage tree, address key_) internal view returns (uint64) { + return + _search( + tree._tree.tree, + tree._tree.root, + bytes32(uint256(uint160(key_))), + _getComparator(tree._tree) + ); } /** @@ -417,45 +422,8 @@ library AvlTree { function getValue( AddressAVL storage tree, address key_ - ) internal view returns (bytes storage) { - require( - _search(tree._tree, _asBytes32(key_)), - "AvlTree: node with such key doesn't exist" - ); - return tree._tree.tree[_asBytes32(key_)].value; - } - - /** - * @notice The function to retrieve the minimum key in the address tree. - * Complexity is O(log(n)), where n is the number of elements in the tree. - * - * @param tree self. - * @return The minimum key in the tree. - */ - function getMin(AddressAVL storage tree) internal view returns (address) { - return _asAddress(_getMin(tree._tree.tree, tree._tree.root)); - } - - /** - * @notice The function to retrieve the maximum key in the address tree. - * Complexity is O(log(n)), where n is the number of elements in the tree. - * - * @param tree self. - * @return The maximum key in the tree. - */ - function getMax(AddressAVL storage tree) internal view returns (address) { - return _asAddress(_getMax(tree._tree.tree, tree._tree.root)); - } - - /** - * @notice The function to return the key of the root element of the address tree. - * Complexity is O(1). - * - * @param tree self. - * @return The key of the root element of the uint256 tree. - */ - function root(AddressAVL storage tree) internal view returns (address) { - return _asAddress(tree._tree.root); + ) internal view returns (bool, bytes32) { + return _getValue(tree._tree, bytes32(uint256(uint160(key_)))); } /** @@ -463,35 +431,20 @@ library AvlTree { * @param tree self. * @return The size of the tree. */ - function treeSize(AddressAVL storage tree) internal view returns (uint256) { - return tree._tree.treeSize; + function treeSize(AddressAVL storage tree) internal view returns (uint64) { + return uint64(_treeSize(tree._tree)); } - /** - * @notice The function to perform an in-order traversal of the address tree. - * @param tree self. - * @return An array of keys in in-order traversal. - */ - function inOrderTraversal(AddressAVL storage tree) internal view returns (address[] memory) { - return _getTraversal(tree._tree, _inOrderTraversal).asAddressArray(); + function beginTraversal( + AddressAVL storage tree + ) internal view returns (Traversal.Iterator memory) { + return _beginTraversal(tree._tree); } - /** - * @notice The function to perform an pre-order traversal of the address tree. - * @param tree self. - * @return An array of keys in pre-order traversal. - */ - function preOrderTraversal(AddressAVL storage tree) internal view returns (address[] memory) { - return _getTraversal(tree._tree, _preOrderTraversal).asAddressArray(); - } - - /** - * @notice The function to perform an post-order traversal of the address tree. - * @param tree self. - * @return An array of keys in post-order traversal. - */ - function postOrderTraversal(AddressAVL storage tree) internal view returns (address[] memory) { - return _getTraversal(tree._tree, _postOrderTraversal).asAddressArray(); + function endTraversal( + AddressAVL storage tree + ) internal view returns (Traversal.Iterator memory) { + return _endTraversal(tree._tree); } /** @@ -511,111 +464,167 @@ library AvlTree { struct Node { bytes32 key; - bytes value; - uint256 height; - bytes32 left; - bytes32 right; + bytes32 value; + uint64 height; + uint64 parent; + uint64 left; + uint64 right; } struct Tree { - bytes32 root; - uint256 treeSize; + uint64 root; + uint64 totalCount; + uint64 removedCount; bool isCustomComparatorSet; - mapping(bytes32 => Node) tree; - function(bytes32, bytes32, bytes memory, bytes memory) view returns (int8) comparator; + mapping(uint64 => Node) tree; + function(bytes32, bytes32) view returns (int8) comparator; } function _setComparator( Tree storage tree, - function(bytes32, bytes32, bytes memory, bytes memory) view returns (int8) comparator_ + function(bytes32, bytes32) view returns (int8) comparator_ ) private { - require(tree.treeSize == 0, "AvlTree: the tree must be empty"); + require(_treeSize(tree) == 0, "AvlTree: the tree must be empty"); tree.isCustomComparatorSet = true; tree.comparator = comparator_; } - function _insert(Tree storage tree, bytes32 key_, bytes memory value_) private { + function _insert(Tree storage tree, bytes32 key_, bytes32 value_) private { require(key_ != 0, "AvlTree: key is not allowed to be 0"); - require(tree.tree[key_].key != key_, "AvlTree: the node already exists"); + require( + _search(tree.tree, tree.root, key_, _getComparator(tree)) == 0, + "AvlTree: the node already exists" + ); - tree.root = _insertNode(tree.tree, tree.root, key_, value_, _getComparator(tree)); + tree.totalCount++; - tree.treeSize++; + tree.root = _insertNode( + tree.tree, + tree.totalCount, + tree.root, + 0, + key_, + value_, + _getComparator(tree) + ); } function _remove(Tree storage tree, bytes32 key_) private { require(key_ != 0, "AvlTree: key is not allowed to be 0"); - require(tree.tree[key_].key == key_, "AvlTree: the node doesn't exist"); + require( + _search(tree.tree, tree.root, key_, _getComparator(tree)) != 0, + "AvlTree: the node doesn't exist" + ); - tree.root = _removeNode(tree.tree, tree.root, key_, _getComparator(tree)); + tree.root = _removeNode(tree.tree, tree.root, 0, bytes32(key_), _getComparator(tree)); - tree.treeSize--; + tree.removedCount++; } function _insertNode( - mapping(bytes32 => Node) storage _tree, - bytes32 node_, + mapping(uint64 => Node) storage _tree, + uint64 index_, + uint64 node_, + uint64 parent_, bytes32 key_, - bytes memory value_, - function(bytes32, bytes32, bytes memory, bytes memory) view returns (int8) comparator_ - ) private returns (bytes32) { - if (node_ == 0) { - _tree[key_] = Node({key: key_, value: value_, left: 0, right: 0, height: 1}); + bytes32 value_, + function(bytes32, bytes32) view returns (int8) comparator_ + ) private returns (uint64) { + if (_tree[node_].key == 0) { + _tree[index_] = Node({ + key: key_, + value: value_, + parent: parent_, + left: 0, + right: 0, + height: 1 + }); - return key_; + return index_; } - if (comparator_(key_, node_, value_, _tree[node_].value) <= 0) { - _tree[node_].left = _insertNode(_tree, _tree[node_].left, key_, value_, comparator_); + if (comparator_(key_, _tree[node_].key) <= 0) { + _tree[node_].left = _insertNode( + _tree, + index_, + _tree[node_].left, + node_, + key_, + value_, + comparator_ + ); } else { - _tree[node_].right = _insertNode(_tree, _tree[node_].right, key_, value_, comparator_); + _tree[node_].right = _insertNode( + _tree, + index_, + _tree[node_].right, + node_, + key_, + value_, + comparator_ + ); } return _balance(_tree, node_); } function _removeNode( - mapping(bytes32 => Node) storage _tree, - bytes32 node_, + mapping(uint64 => Node) storage _tree, + uint64 node_, + uint64 parent_, bytes32 key_, - function(bytes32, bytes32, bytes memory, bytes memory) view returns (int8) comparator_ - ) private returns (bytes32) { - if (comparator_(key_, node_, _tree[key_].value, _tree[node_].value) == 0) { - bytes32 left_ = _tree[node_].left; - bytes32 right_ = _tree[node_].right; + function(bytes32, bytes32) view returns (int8) comparator_ + ) private returns (uint64) { + int8 comparison_ = comparator_(key_, _tree[node_].key); + + if (comparison_ == 0) { + uint64 left_ = _tree[node_].left; + uint64 right_ = _tree[node_].right; delete _tree[node_]; if (right_ == 0) { + if (left_ != 0) { + _tree[left_].parent = parent_; + } + return left_; } - bytes32 temp_; + uint64 temp_; for (temp_ = right_; _tree[temp_].left != 0; temp_ = _tree[temp_].left) {} _tree[temp_].right = _removeMin(_tree, right_); _tree[temp_].left = left_; + if (left_ != 0) { + _tree[left_].parent = temp_; + } + return _balance(_tree, temp_); - } else if (comparator_(key_, node_, _tree[key_].value, _tree[node_].value) < 0) { - _tree[node_].left = _removeNode(_tree, _tree[node_].left, key_, comparator_); + } else if (comparison_ < 0) { + _tree[node_].left = _removeNode(_tree, _tree[node_].left, node_, key_, comparator_); } else { - _tree[node_].right = _removeNode(_tree, _tree[node_].right, key_, comparator_); + _tree[node_].right = _removeNode(_tree, _tree[node_].right, node_, key_, comparator_); } return _balance(_tree, node_); } function _removeMin( - mapping(bytes32 => Node) storage _tree, - bytes32 node_ - ) private returns (bytes32) { + mapping(uint64 => Node) storage _tree, + uint64 node_ + ) private returns (uint64) { Node storage _node = _tree[node_]; if (_node.left == 0) { + if (_node.right != 0) { + _tree[_node.right].parent = _node.parent; + } + return _node.right; } @@ -625,12 +634,20 @@ library AvlTree { } function _rotateLeft( - mapping(bytes32 => Node) storage _tree, - bytes32 node_ - ) private returns (bytes32) { + mapping(uint64 => Node) storage _tree, + uint64 node_ + ) private returns (uint64) { Node storage _node = _tree[node_]; - bytes32 temp_ = _node.left; + uint64 temp_ = _node.left; + + _tree[temp_].parent = _node.parent; + _node.parent = temp_; + + if (_tree[temp_].right != 0) { + _tree[_tree[temp_].right].parent = node_; + } + _node.left = _tree[temp_].right; _tree[temp_].right = node_; @@ -641,12 +658,20 @@ library AvlTree { } function _rotateRight( - mapping(bytes32 => Node) storage _tree, - bytes32 node_ - ) private returns (bytes32) { + mapping(uint64 => Node) storage _tree, + uint64 node_ + ) private returns (uint64) { Node storage _node = _tree[node_]; - bytes32 temp_ = _node.right; + uint64 temp_ = _node.right; + + _tree[temp_].parent = _node.parent; + _node.parent = temp_; + + if (_tree[temp_].left != 0) { + _tree[_tree[temp_].left].parent = node_; + } + _node.right = _tree[temp_].left; _tree[temp_].left = node_; @@ -657,9 +682,9 @@ library AvlTree { } function _balance( - mapping(bytes32 => Node) storage _tree, - bytes32 node_ - ) private returns (bytes32) { + mapping(uint64 => Node) storage _tree, + uint64 node_ + ) private returns (uint64) { _updateHeight(_tree, node_); Node storage _left = _tree[_tree[node_].left]; @@ -682,142 +707,91 @@ library AvlTree { return node_; } - function _updateHeight(mapping(bytes32 => Node) storage _tree, bytes32 node_) private { + function _updateHeight(mapping(uint64 => Node) storage _tree, uint64 node_) private { Node storage _node = _tree[node_]; - _node.height = 1 + Math.max(_tree[_node.left].height, _tree[_node.right].height); - } - - function _search(Tree storage tree, bytes32 key_) private view returns (bool) { - return key_ != 0 && tree.tree[key_].key == key_; + _node.height = uint64(1 + Math.max(_tree[_node.left].height, _tree[_node.right].height)); } - function _getMin( - mapping(bytes32 => Node) storage _tree, - bytes32 node_ - ) private view returns (bytes32) { - while (_tree[node_].left != 0) { - node_ = _tree[node_].left; + function _search( + mapping(uint64 => Node) storage _tree, + uint64 node_, + bytes32 key_, + function(bytes32, bytes32) view returns (int8) comparator_ + ) private view returns (uint64) { + if (node_ == 0) { + return 0; } - return node_; - } + int8 comparison_ = comparator_(key_, _tree[node_].key); - function _getMax( - mapping(bytes32 => Node) storage _tree, - bytes32 node_ - ) private view returns (bytes32) { - while (_tree[node_].right != 0) { - node_ = _tree[node_].right; + if (comparison_ == 0) { + return node_; + } else if (comparison_ < 0) { + return _search(_tree, _tree[node_].left, key_, comparator_); + } else { + return _search(_tree, _tree[node_].right, key_, comparator_); } - - return node_; } - function _getTraversal( - Tree storage tree, - function(mapping(bytes32 => Node) storage, bytes32, bytes32[] memory, uint256) - view - returns (uint256) traversalFunction_ - ) private view returns (bytes32[] memory) { - bytes32[] memory keys_ = new bytes32[](tree.treeSize); + function _getValue(Tree storage tree, bytes32 key_) private view returns (bool, bytes32) { + uint64 index_ = _search(tree.tree, tree.root, key_, _getComparator(tree)); - traversalFunction_(tree.tree, tree.root, keys_, 0); + if (index_ == 0) { + return (false, 0); + } - return keys_; + return (true, tree.tree[index_].value); } - function _inOrderTraversal( - mapping(bytes32 => Node) storage _tree, - bytes32 node_, - bytes32[] memory keys_, - uint256 index_ - ) private view returns (uint256) { - if (node_ == 0) { - return index_; - } - - index_ = _inOrderTraversal(_tree, _tree[node_].left, keys_, index_); + function _treeSize(Tree storage tree) private view returns (uint256) { + return tree.totalCount - tree.removedCount; + } - if (_tree[node_].key != 0) { - keys_[index_++] = _tree[node_].key; + function _beginTraversal(Tree storage tree) private view returns (Traversal.Iterator memory) { + uint256 treeMappingSlot_; + assembly { + treeMappingSlot_ := add(tree.slot, 1) } - index_ = _inOrderTraversal(_tree, _tree[node_].right, keys_, index_); + uint64 root_ = tree.root; - return index_; - } - - function _preOrderTraversal( - mapping(bytes32 => Node) storage _tree, - bytes32 node_, - bytes32[] memory keys_, - uint256 index_ - ) private view returns (uint256) { - if (node_ == 0) { - return index_; + if (root_ == 0) { + return Traversal.Iterator({treeMappingSlot: treeMappingSlot_, currentNode: 0}); } - if (_tree[node_].key != 0) { - keys_[index_++] = _tree[node_].key; + uint64 current_ = root_; + while (tree.tree[current_].left != 0) { + current_ = tree.tree[current_].left; } - index_ = _preOrderTraversal(_tree, _tree[node_].left, keys_, index_); - index_ = _preOrderTraversal(_tree, _tree[node_].right, keys_, index_); - - return index_; + return Traversal.Iterator({treeMappingSlot: treeMappingSlot_, currentNode: current_}); } - function _postOrderTraversal( - mapping(bytes32 => Node) storage _tree, - bytes32 node_, - bytes32[] memory keys_, - uint256 index_ - ) private view returns (uint256) { - if (node_ == 0) { - return index_; - } - - index_ = _postOrderTraversal(_tree, _tree[node_].left, keys_, index_); - index_ = _postOrderTraversal(_tree, _tree[node_].right, keys_, index_); - - if (_tree[node_].key != 0) { - keys_[index_++] = _tree[node_].key; + function _endTraversal(Tree storage tree) private pure returns (Traversal.Iterator memory) { + uint256 treeMappingSlot_; + assembly { + treeMappingSlot_ := add(tree.slot, 1) } - return index_; + return Traversal.Iterator({treeMappingSlot: treeMappingSlot_, currentNode: 0}); } function _getComparator( Tree storage tree - ) - private - view - returns (function(bytes32, bytes32, bytes memory, bytes memory) view returns (int8)) - { + ) private view returns (function(bytes32, bytes32) view returns (int8)) { return tree.isCustomComparatorSet ? tree.comparator : _defaultComparator; } - function _defaultComparator( - bytes32 key1_, - bytes32 key2_, - bytes memory, - bytes memory - ) private pure returns (int8) { - if (key1_ < key2_) return -1; - if (key1_ > key2_) return 1; - return 0; - } - - function _asAddress(bytes32 from_) private pure returns (address to_) { - assembly { - to_ := from_ + function _defaultComparator(bytes32 key1_, bytes32 key2_) private pure returns (int8) { + if (key1_ < key2_) { + return -1; } - } - function _asBytes32(address from_) private pure returns (bytes32 to_) { - assembly { - to_ := from_ + if (key1_ > key2_) { + return 1; } + + return 0; } } diff --git a/contracts/mock/libs/data-structures/AvlTreeMock.sol b/contracts/mock/libs/data-structures/AvlTreeMock.sol index ad51234d..53ce6e08 100644 --- a/contracts/mock/libs/data-structures/AvlTreeMock.sol +++ b/contracts/mock/libs/data-structures/AvlTreeMock.sol @@ -1,16 +1,11 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.4; -import {AvlTree} from "../../../libs/data-structures/AvlTree.sol"; +import {Traversal, AvlTree} from "../../../libs/data-structures/AvlTree.sol"; contract AvlTreeMock { using AvlTree for *; - - struct customStruct { - uint256 valueUint; - bool valueBool; - string valueString; - } + using Traversal for *; AvlTree.UintAVL internal _uintTree; AvlTree.Bytes32AVL internal _bytes32Tree; @@ -20,52 +15,28 @@ contract AvlTreeMock { _uintTree.setComparator(_descComparator); } - function setUintValueComparatorUint() external { - _uintTree.setComparator(_valueComparatorUint); - } - - function setUintValueComparatorString() external { - _uintTree.setComparator(_valueComparatorString); - } - - function setUintValueComparatorStruct() external { - _uintTree.setComparator(_structComparator); - } - function setBytes32DescComparator() external { _bytes32Tree.setComparator(_descComparator); } - function setUintValueComparatorAddress() external { - _addressTree.setComparator(_valueComparatorUint); - } - function setAddressDescComparator() external { _addressTree.setComparator(_descComparator); } function insertUintToUint(uint256 key_, uint256 value_) external { - _uintTree.insert(key_, abi.encode(value_)); + _uintTree.insert(key_, bytes32(value_)); } function insertAddressToUint(uint256 key_, address value_) external { - _uintTree.insert(key_, abi.encode(value_)); - } - - function insertStringToUint(uint256 key_, string calldata value_) external { - _uintTree.insert(key_, abi.encode(value_)); - } - - function insertStructToUint(uint256 key_, bytes calldata structBytes_) external { - _uintTree.insert(key_, structBytes_); + _uintTree.insert(key_, bytes32(uint256(uint160(value_)))); } function insertUintToBytes32(bytes32 key_, uint256 value_) external { - _bytes32Tree.insert(key_, abi.encode(value_)); + _bytes32Tree.insert(key_, bytes32(value_)); } function insertUintToAddress(address key_, uint256 value_) external { - _addressTree.insert(key_, abi.encode(value_)); + _addressTree.insert(key_, bytes32(value_)); } function removeUint(uint256 key_) external { @@ -80,120 +51,251 @@ contract AvlTreeMock { _addressTree.remove(key_); } - function searchUint(uint256 key_) external view returns (bool) { + function searchUint(uint256 key_) external view returns (uint64) { return _uintTree.search(key_); } - function searchBytes32(bytes32 key_) external view returns (bool) { + function searchBytes32(bytes32 key_) external view returns (uint64) { return _bytes32Tree.search(key_); } - function searchAddress(address key_) external view returns (bool) { + function searchAddress(address key_) external view returns (uint64) { return _addressTree.search(key_); } - function getUintValueUint(uint256 key_) external view returns (uint256) { - return abi.decode(_uintTree.getValue(key_), (uint256)); - } + function getUintValueUint(uint256 key_) external view returns (bool, uint256) { + (bool exists_, bytes32 value_) = _uintTree.getValue(key_); - function getAddressValueUint(uint256 key_) external view returns (address) { - return abi.decode(_uintTree.getValue(key_), (address)); + return (exists_, uint256(value_)); } - function getStringValueUint(uint256 key_) external view returns (string memory) { - return abi.decode(_uintTree.getValue(key_), (string)); - } + function getAddressValueUint(uint256 key_) external view returns (bool, address) { + (bool exists_, bytes32 value_) = _uintTree.getValue(key_); - function getUintValueBytes32(bytes32 key_) external view returns (uint256) { - return abi.decode(_bytes32Tree.getValue(key_), (uint256)); + return (exists_, address(uint160(uint256(value_)))); } - function getUintValueAddress(address key_) external view returns (uint256) { - return abi.decode(_addressTree.getValue(key_), (uint256)); - } + function getUintValueBytes32(bytes32 key_) external view returns (bool, uint256) { + (bool exists_, bytes32 value_) = _bytes32Tree.getValue(key_); - function getMinUint() external view returns (uint256) { - return _uintTree.getMin(); + return (exists_, uint256(value_)); } - function getMinBytes32() external view returns (bytes32) { - return _bytes32Tree.getMin(); - } + function getUintValueAddress(address key_) external view returns (bool, uint256) { + (bool exists_, bytes32 value_) = _addressTree.getValue(key_); - function getMinAddress() external view returns (address) { - return _addressTree.getMin(); + return (exists_, uint256(value_)); } - function getMaxUint() external view returns (uint256) { - return _uintTree.getMax(); + function treeSizeUint() external view returns (uint64) { + return _uintTree.treeSize(); } - function getMaxBytes32() external view returns (bytes32) { - return _bytes32Tree.getMax(); + function treeSizeBytes32() external view returns (uint64) { + return _bytes32Tree.treeSize(); } - function getMaxAddress() external view returns (address) { - return _addressTree.getMax(); + function treeSizeAddress() external view returns (uint64) { + return _addressTree.treeSize(); } function rootUint() external view returns (uint256) { - return _uintTree.root(); + return uint256(_uintTree._tree.tree[_uintTree._tree.root].key); } function rootBytes32() external view returns (bytes32) { - return _bytes32Tree.root(); + return _bytes32Tree._tree.tree[_bytes32Tree._tree.root].key; } function rootAddress() external view returns (address) { - return _addressTree.root(); + return address(uint160(uint256(_addressTree._tree.tree[_addressTree._tree.root].key))); } - function treeSizeUint() external view returns (uint256) { - return _uintTree.treeSize(); - } + function traverseUint() external view returns (uint256[] memory, uint256[] memory) { + Traversal.Iterator memory iterator_ = _uintTree.beginTraversal(); - function treeSizeBytes32() external view returns (uint256) { - return _bytes32Tree.treeSize(); - } + uint256[] memory keys_ = new uint256[](_uintTree.treeSize()); + uint256[] memory values_ = new uint256[](_uintTree.treeSize()); - function treeSizeAddress() external view returns (uint256) { - return _addressTree.treeSize(); - } + if (keys_.length != 0) { + (bytes32 bytesKey_, bytes32 bytesValue_) = iterator_.value(); - function inOrderTraversalUint() external view returns (uint256[] memory) { - return _uintTree.inOrderTraversal(); - } + keys_[0] = uint256(bytesKey_); + values_[0] = uint256(bytesValue_); + } - function inOrderTraversalBytes32() external view returns (bytes32[] memory) { - return _bytes32Tree.inOrderTraversal(); - } + uint256 index_ = 1; - function inOrderTraversalAddress() external view returns (address[] memory) { - return _addressTree.inOrderTraversal(); + while (iterator_.hasNext()) { + (bytes32 bytesKey_, bytes32 bytesValue_) = iterator_.next(); + + keys_[index_] = uint256(bytesKey_); + values_[index_] = uint256(bytesValue_); + + index_++; + } + + return (keys_, values_); } - function preOrderTraversalUint() external view returns (uint256[] memory) { - return _uintTree.preOrderTraversal(); + function traverseFirstThreeUint() external view returns (uint256[] memory, uint256[] memory) { + Traversal.Iterator memory iterator_ = _uintTree.beginTraversal(); + + uint256[] memory keys_ = new uint256[](3); + uint256[] memory values_ = new uint256[](3); + + if (keys_.length != 0) { + (bytes32 bytesKey_, bytes32 bytesValue_) = iterator_.value(); + + keys_[0] = uint256(bytesKey_); + values_[0] = uint256(bytesValue_); + } + + uint256 index_ = 1; + + while (iterator_.hasNext()) { + (bytes32 bytesKey_, bytes32 bytesValue_) = iterator_.next(); + + keys_[index_] = uint256(bytesKey_); + values_[index_] = uint256(bytesValue_); + + if (index_++ == 2) { + iterator_ = _uintTree.endTraversal(); + } + } + + return (keys_, values_); } - function preOrderTraversalBytes32() external view returns (bytes32[] memory) { - return _bytes32Tree.preOrderTraversal(); + function brokenTraversalUint() external view { + Traversal.Iterator memory iterator_ = _uintTree.beginTraversal(); + + uint256[] memory keys_ = new uint256[](_uintTree.treeSize()); + uint256[] memory values_ = new uint256[](_uintTree.treeSize()); + + (bytes32 bytesKey_, bytes32 bytesValue_) = iterator_.value(); + + keys_[0] = uint256(bytesKey_); + values_[0] = uint256(bytesValue_); + + uint256 index_ = 1; + + while (iterator_.hasNext()) { + iterator_.currentNode = 0; + + (bytesKey_, bytesValue_) = iterator_.next(); + + keys_[index_] = uint256(bytesKey_); + values_[index_] = uint256(bytesValue_); + } } - function preOrderTraversalAddress() external view returns (address[] memory) { - return _addressTree.preOrderTraversal(); + function traverseBytes32() external view returns (bytes32[] memory, bytes32[] memory) { + Traversal.Iterator memory iterator_ = _bytes32Tree.beginTraversal(); + + bytes32[] memory keys_ = new bytes32[](_bytes32Tree.treeSize()); + bytes32[] memory values_ = new bytes32[](_bytes32Tree.treeSize()); + + if (keys_.length != 0) { + (keys_[0], values_[0]) = iterator_.value(); + } + + uint256 index_ = 1; + + while (iterator_.hasNext()) { + (keys_[index_], values_[index_]) = iterator_.next(); + + index_++; + } + + return (keys_, values_); } - function postOrderTraversalUint() external view returns (uint256[] memory) { - return _uintTree.postOrderTraversal(); + function traverseFirstThreeBytes32() + external + view + returns (bytes32[] memory, bytes32[] memory) + { + Traversal.Iterator memory iterator_ = _bytes32Tree.beginTraversal(); + + bytes32[] memory keys_ = new bytes32[](3); + bytes32[] memory values_ = new bytes32[](3); + + if (keys_.length != 0) { + (keys_[0], values_[0]) = iterator_.value(); + } + + uint256 index_ = 1; + + while (iterator_.hasNext()) { + (keys_[index_], values_[index_]) = iterator_.next(); + + if (index_++ == 2) { + iterator_ = _bytes32Tree.endTraversal(); + } + } + + return (keys_, values_); } - function postOrderTraversalBytes32() external view returns (bytes32[] memory) { - return _bytes32Tree.postOrderTraversal(); + function traverseAddress() external view returns (address[] memory, address[] memory) { + Traversal.Iterator memory iterator_ = _addressTree.beginTraversal(); + + address[] memory keys_ = new address[](_addressTree.treeSize()); + address[] memory values_ = new address[](_addressTree.treeSize()); + + if (keys_.length != 0) { + (bytes32 bytesKey_, bytes32 bytesValue_) = iterator_.value(); + + keys_[0] = address(uint160(uint256(bytesKey_))); + values_[0] = address(uint160(uint256(bytesValue_))); + } + + uint256 index_ = 1; + + while (iterator_.hasNext()) { + (bytes32 bytesKey_, bytes32 bytesValue_) = iterator_.next(); + + keys_[index_] = address(uint160(uint256(bytesKey_))); + values_[index_] = address(uint160(uint256(bytesValue_))); + + index_++; + } + + return (keys_, values_); } - function postOrderTraversalAddress() external view returns (address[] memory) { - return _addressTree.postOrderTraversal(); + function traverseFirstThreeAddress() + external + view + returns (address[] memory, address[] memory) + { + Traversal.Iterator memory iterator_ = _addressTree.beginTraversal(); + + address[] memory keys_ = new address[](3); + address[] memory values_ = new address[](3); + + if (keys_.length != 0) { + (bytes32 bytesKey_, bytes32 bytesValue_) = iterator_.value(); + + keys_[0] = address(uint160(uint256(bytesKey_))); + values_[0] = address(uint160(uint256(bytesValue_))); + } + + uint256 index_ = 1; + + while (iterator_.hasNext()) { + (bytes32 bytesKey_, bytes32 bytesValue_) = iterator_.next(); + + keys_[index_] = address(uint160(uint256(bytesKey_))); + values_[index_] = address(uint160(uint256(bytesValue_))); + + if (index_++ == 2) { + iterator_ = _addressTree.endTraversal(); + } + } + + return (keys_, values_); } function isCustomComparatorSetUint() external view returns (bool) { @@ -208,65 +310,15 @@ contract AvlTreeMock { return _addressTree.isCustomComparatorSet(); } - function valueToBytesStruct(uint256 value_) external pure returns (bytes memory) { - return abi.encode(customStruct(value_, true, "test")); - } - - function _descComparator( - bytes32 key1_, - bytes32 key2_, - bytes memory, - bytes memory - ) private pure returns (int8) { - if (key1_ > key2_) return -1; - if (key1_ < key2_) return 1; - return 0; - } - - function _valueComparatorUint( - bytes32, - bytes32, - bytes memory value1_, - bytes memory value2_ - ) private pure returns (int8) { - uint256 aValue = abi.decode(value1_, (uint256)); - uint256 bValue = abi.decode(value2_, (uint256)); - - if (aValue < bValue) return -1; - if (aValue > bValue) return 1; - return 0; - } - - function _valueComparatorString( - bytes32, - bytes32, - bytes memory value1_, - bytes memory value2_ - ) private pure returns (int8) { - uint256 minLength = value1_.length; - if (value2_.length < minLength) minLength = value2_.length; - - for (uint i = 0; i < minLength; i++) { - if (value1_[i] < value2_[i]) return -1; - else if (value1_[i] > value2_[i]) return 1; + function _descComparator(bytes32 key1_, bytes32 key2_) private pure returns (int8) { + if (key1_ > key2_) { + return -1; } - if (value1_.length < value2_.length) return -1; - else if (value1_.length > value2_.length) return 1; - else return 0; - } - - function _structComparator( - bytes32, - bytes32, - bytes memory value1_, - bytes memory value2_ - ) private pure returns (int8) { - customStruct memory aStruct_ = abi.decode(value1_, (customStruct)); - customStruct memory bStruct_ = abi.decode(value2_, (customStruct)); + if (key1_ < key2_) { + return 1; + } - if (aStruct_.valueUint < bStruct_.valueUint) return -1; - if (aStruct_.valueUint > bStruct_.valueUint) return 1; return 0; } } diff --git a/test/libs/data-structures/AvlTree.test.ts b/test/libs/data-structures/AvlTree.test.ts index 9fa065cf..2b4b124f 100644 --- a/test/libs/data-structures/AvlTree.test.ts +++ b/test/libs/data-structures/AvlTree.test.ts @@ -42,44 +42,46 @@ describe("AvlTree", () => { describe("Uint Tree", () => { describe("insert", () => { it("should insert uint values to the uint tree correctly", async () => { - await avlTree.setUintValueComparatorUint(); + await avlTree.insertUintToUint(4, 1); + await avlTree.insertUintToUint(12, 2); + await avlTree.insertUintToUint(1, 3); - await avlTree.insertUintToUint(1, 4); - await avlTree.insertUintToUint(2, 12); - await avlTree.insertUintToUint(3, 1); - - expect(await avlTree.rootUint()).to.equal(1); + expect(await avlTree.rootUint()).to.equal(4); expect(await avlTree.treeSizeUint()).to.equal(3); - expect(await avlTree.inOrderTraversalUint()).to.deep.equal([3, 1, 2]); - expect(await avlTree.preOrderTraversalUint()).to.deep.equal([1, 3, 2]); - expect(await avlTree.postOrderTraversalUint()).to.deep.equal([3, 2, 1]); - await avlTree.insertUintToUint(4, 200); - await avlTree.insertUintToUint(5, 10); - await avlTree.insertUintToUint(6, 5); + let traversal = await avlTree.traverseUint(); + expect(traversal[0]).to.deep.equal([1, 4, 12]); + expect(traversal[1]).to.deep.equal([3, 1, 2]); + + await avlTree.insertUintToUint(200, 4); + await avlTree.insertUintToUint(10, 5); + await avlTree.insertUintToUint(5, 6); - expect(await avlTree.rootUint()).to.equal(5); + expect(await avlTree.rootUint()).to.equal(10); expect(await avlTree.treeSizeUint()).to.equal(6); - expect(await avlTree.inOrderTraversalUint()).to.deep.equal([3, 1, 6, 5, 2, 4]); - expect(await avlTree.preOrderTraversalUint()).to.deep.equal([5, 1, 3, 6, 2, 4]); - expect(await avlTree.postOrderTraversalUint()).to.deep.equal([3, 6, 1, 4, 2, 5]); - await avlTree.insertUintToUint(7, 15); + traversal = await avlTree.traverseUint(); + expect(traversal[0]).to.deep.equal([1, 4, 5, 10, 12, 200]); + expect(traversal[1]).to.deep.equal([3, 1, 6, 5, 2, 4]); - expect(await avlTree.rootUint()).to.equal(5); + await avlTree.insertUintToUint(15, 7); + + expect(await avlTree.rootUint()).to.equal(10); expect(await avlTree.treeSizeUint()).to.equal(7); - expect(await avlTree.inOrderTraversalUint()).to.deep.equal([3, 1, 6, 5, 2, 7, 4]); - expect(await avlTree.preOrderTraversalUint()).to.deep.equal([5, 1, 3, 6, 7, 2, 4]); - expect(await avlTree.postOrderTraversalUint()).to.deep.equal([3, 6, 1, 2, 4, 7, 5]); - await avlTree.insertUintToUint(8, 2); - await avlTree.insertUintToUint(9, 3); + traversal = await avlTree.traverseUint(); + expect(traversal[0]).to.deep.equal([1, 4, 5, 10, 12, 15, 200]); + expect(traversal[1]).to.deep.equal([3, 1, 6, 5, 2, 7, 4]); + + await avlTree.insertUintToUint(2, 8); + await avlTree.insertUintToUint(3, 9); - expect(await avlTree.rootUint()).to.equal(5); + expect(await avlTree.rootUint()).to.equal(10); expect(await avlTree.treeSizeUint()).to.equal(9); - expect(await avlTree.inOrderTraversalUint()).to.deep.equal([3, 8, 9, 1, 6, 5, 2, 7, 4]); - expect(await avlTree.preOrderTraversalUint()).to.deep.equal([5, 1, 8, 3, 9, 6, 7, 2, 4]); - expect(await avlTree.postOrderTraversalUint()).to.deep.equal([3, 9, 8, 6, 1, 2, 4, 7, 5]); + + traversal = await avlTree.traverseUint(); + expect(traversal[0]).to.deep.equal([1, 2, 3, 4, 5, 10, 12, 15, 200]); + expect(traversal[1]).to.deep.equal([3, 8, 9, 1, 6, 5, 2, 7, 4]); }); it("should insert address values to the uint tree correctly", async () => { @@ -89,9 +91,10 @@ describe("AvlTree", () => { expect(await avlTree.rootUint()).to.equal(4); expect(await avlTree.treeSizeUint()).to.equal(3); - expect(await avlTree.inOrderTraversalUint()).to.deep.equal([2, 4, 6]); - expect(await avlTree.preOrderTraversalUint()).to.deep.equal([4, 2, 6]); - expect(await avlTree.postOrderTraversalUint()).to.deep.equal([2, 6, 4]); + + let traversal = await avlTree.traverseUint(); + expect(traversal[0]).to.deep.equal([2, 4, 6]); + expect(traversal[1]).to.deep.equal([USER2.address, USER2.address, USER1.address]); await avlTree.insertAddressToUint(1, USER2); await avlTree.insertAddressToUint(13, USER4); @@ -100,78 +103,58 @@ describe("AvlTree", () => { expect(await avlTree.rootUint()).to.equal(4); expect(await avlTree.treeSizeUint()).to.equal(7); - expect(await avlTree.inOrderTraversalUint()).to.deep.equal([1, 2, 4, 5, 6, 7, 13]); - expect(await avlTree.preOrderTraversalUint()).to.deep.equal([4, 2, 1, 7, 6, 5, 13]); - expect(await avlTree.postOrderTraversalUint()).to.deep.equal([1, 2, 5, 6, 13, 7, 4]); - - await avlTree.insertAddressToUint(9, USER4); - await avlTree.insertAddressToUint(8, USER4); - expect(await avlTree.rootUint()).to.equal(4); - expect(await avlTree.treeSizeUint()).to.equal(9); - expect(await avlTree.inOrderTraversalUint()).to.deep.equal([1, 2, 4, 5, 6, 7, 8, 9, 13]); - expect(await avlTree.preOrderTraversalUint()).to.deep.equal([4, 2, 1, 7, 6, 5, 9, 8, 13]); - expect(await avlTree.postOrderTraversalUint()).to.deep.equal([1, 2, 5, 6, 8, 13, 9, 7, 4]); + traversal = await avlTree.traverseUint(); + expect(traversal[0]).to.deep.equal([1, 2, 4, 5, 6, 7, 13]); + expect(traversal[1]).to.deep.equal([ + USER2.address, + USER2.address, + USER2.address, + USER3.address, + USER1.address, + USER1.address, + USER4.address, + ]); }); - it("should insert string values to the uint tree correctly", async () => { - await avlTree.setUintValueComparatorString(); - - await avlTree.insertStringToUint(1, "b"); - await avlTree.insertStringToUint(2, "f"); - await avlTree.insertStringToUint(3, "g"); - await avlTree.insertStringToUint(4, "a"); - await avlTree.insertStringToUint(5, "d"); - - expect(await avlTree.rootUint()).to.equal(2); - expect(await avlTree.treeSizeUint()).to.equal(5); - expect(await avlTree.inOrderTraversalUint()).to.deep.equal([4, 1, 5, 2, 3]); - expect(await avlTree.preOrderTraversalUint()).to.deep.equal([2, 1, 4, 5, 3]); - expect(await avlTree.postOrderTraversalUint()).to.deep.equal([4, 5, 1, 3, 2]); - - await avlTree.insertStringToUint(6, "e"); - await avlTree.insertStringToUint(7, "c"); + it("should handle complex operations on the uint tree", async () => { + await avlTree.insertUintToUint(2, 10); + await avlTree.insertUintToUint(3, 1); + await avlTree.insertUintToUint(10, 18); - expect(await avlTree.rootUint()).to.equal(5); - expect(await avlTree.treeSizeUint()).to.equal(7); - expect(await avlTree.inOrderTraversalUint()).to.deep.equal([4, 1, 7, 5, 6, 2, 3]); - expect(await avlTree.preOrderTraversalUint()).to.deep.equal([5, 1, 4, 7, 2, 6, 3]); - expect(await avlTree.postOrderTraversalUint()).to.deep.equal([4, 7, 1, 6, 3, 2, 5]); - }); + await avlTree.removeUint(3); - it("should insert struct values to the uint tree correctly", async () => { - await avlTree.setUintValueComparatorStruct(); + await avlTree.insertUintToUint(4, 20); + await avlTree.insertUintToUint(5, 10); - await avlTree.insertStructToUint(1, await avlTree.valueToBytesStruct(20)); - await avlTree.insertStructToUint(2, await avlTree.valueToBytesStruct(1)); - await avlTree.insertStructToUint(3, await avlTree.valueToBytesStruct(18)); + await avlTree.removeUint(2); + await avlTree.removeUint(10); + await avlTree.removeUint(4); - expect(await avlTree.rootUint()).to.equal(3); - expect(await avlTree.treeSizeUint()).to.equal(3); - expect(await avlTree.inOrderTraversalUint()).to.deep.equal([2, 3, 1]); - expect(await avlTree.preOrderTraversalUint()).to.deep.equal([3, 2, 1]); - expect(await avlTree.postOrderTraversalUint()).to.deep.equal([2, 1, 3]); + await avlTree.insertUintToUint(4, 7); + await avlTree.insertUintToUint(1, 20); + await avlTree.insertUintToUint(2, 10); + await avlTree.insertUintToUint(20, 20); + await avlTree.insertUintToUint(111, 10); - await avlTree.insertStructToUint(4, await avlTree.valueToBytesStruct(17)); - await avlTree.insertStructToUint(5, await avlTree.valueToBytesStruct(16)); + await avlTree.removeUint(20); - expect(await avlTree.rootUint()).to.equal(3); - expect(await avlTree.treeSizeUint()).to.equal(5); - expect(await avlTree.inOrderTraversalUint()).to.deep.equal([2, 5, 4, 3, 1]); - expect(await avlTree.preOrderTraversalUint()).to.deep.equal([3, 5, 2, 4, 1]); - expect(await avlTree.postOrderTraversalUint()).to.deep.equal([2, 4, 5, 1, 3]); + await avlTree.insertUintToUint(16, 20); + await avlTree.insertUintToUint(17, 100); + await avlTree.insertUintToUint(18, 20); + await avlTree.insertUintToUint(19, 250); + await avlTree.insertUintToUint(20, 4); - await avlTree.insertStructToUint(6, await avlTree.valueToBytesStruct(2)); + expect(await avlTree.rootUint()).to.equal(4); + expect(await avlTree.treeSizeUint()).to.equal(10); - expect(await avlTree.rootUint()).to.equal(5); - expect(await avlTree.treeSizeUint()).to.equal(6); - expect(await avlTree.inOrderTraversalUint()).to.deep.equal([2, 6, 5, 4, 3, 1]); - expect(await avlTree.preOrderTraversalUint()).to.deep.equal([5, 2, 6, 3, 4, 1]); - expect(await avlTree.postOrderTraversalUint()).to.deep.equal([6, 2, 4, 1, 3, 5]); + const traversal = await avlTree.traverseUint(); + expect(traversal[0]).to.deep.equal([1, 2, 4, 5, 16, 17, 18, 19, 20, 111]); + expect(traversal[1]).to.deep.equal([20, 10, 7, 10, 20, 100, 20, 250, 4, 10]); }); it("should not allow to insert 0 as key to the uint tree", async () => { - await expect(avlTree.insertStringToUint(0, "test")).to.be.revertedWith("AvlTree: key is not allowed to be 0"); + await expect(avlTree.insertUintToUint(0, 100)).to.be.revertedWith("AvlTree: key is not allowed to be 0"); }); it("should not allow to insert node with duplicate key to the uint tree", async () => { @@ -197,9 +180,10 @@ describe("AvlTree", () => { expect(await avlTree.rootUint()).to.equal(4); expect(await avlTree.treeSizeUint()).to.equal(6); - expect(await avlTree.inOrderTraversalUint()).to.deep.equal([1, 2, 4, 6, 7, 10]); - expect(await avlTree.preOrderTraversalUint()).to.deep.equal([4, 2, 1, 7, 6, 10]); - expect(await avlTree.postOrderTraversalUint()).to.deep.equal([1, 2, 6, 10, 7, 4]); + + const traversal = await avlTree.traverseUint(); + expect(traversal[0]).to.deep.equal([1, 2, 4, 6, 7, 10]); + expect(traversal[1]).to.deep.equal([2, 4, 10, 10, 10, 10]); }); it("should remove multiple nodes in the uint tree correctly", async () => { @@ -218,9 +202,10 @@ describe("AvlTree", () => { expect(await avlTree.rootUint()).to.equal(6); expect(await avlTree.treeSizeUint()).to.equal(5); - expect(await avlTree.inOrderTraversalUint()).to.deep.equal([1, 4, 6, 7, 9]); - expect(await avlTree.preOrderTraversalUint()).to.deep.equal([6, 4, 1, 7, 9]); - expect(await avlTree.postOrderTraversalUint()).to.deep.equal([1, 4, 9, 7, 6]); + + const traversal = await avlTree.traverseUint(); + expect(traversal[0]).to.deep.equal([1, 4, 6, 7, 9]); + expect(traversal[1]).to.deep.equal([2, 20, 11, 1, 1]); }); it("should handle removing all the nodes in the uint tree correctly", async () => { @@ -237,6 +222,10 @@ describe("AvlTree", () => { expect(await avlTree.rootUint()).to.be.equal(0); expect(await avlTree.treeSizeUint()).to.be.equal(0); + + const traversal = await avlTree.traverseUint(); + expect(traversal[0].length).to.be.equal(0); + expect(traversal[1].length).to.be.equal(0); }); it("should not allow to remove a node that doesn't exist in the uint tree", async () => { @@ -273,9 +262,10 @@ describe("AvlTree", () => { expect(await avlTree.rootUint()).to.equal(4); expect(await avlTree.treeSizeUint()).to.equal(6); - expect(await avlTree.inOrderTraversalUint()).to.deep.equal([6, 5, 4, 3, 2, 1]); - expect(await avlTree.preOrderTraversalUint()).to.deep.equal([4, 6, 5, 2, 3, 1]); - expect(await avlTree.postOrderTraversalUint()).to.deep.equal([5, 6, 3, 1, 2, 4]); + + const traversal = await avlTree.traverseUint(); + expect(traversal[0]).to.deep.equal([6, 5, 4, 3, 2, 1]); + expect(traversal[1]).to.deep.equal([4, 1, 10, 200, 3, 4]); }); it("should not allow to set comparator for the uint tree if the tree is not empty", async () => { @@ -290,19 +280,19 @@ describe("AvlTree", () => { await avlTree.insertUintToUint(2, 20); await avlTree.insertUintToUint(1, 10); - expect(await avlTree.searchUint(2)).to.be.true; + expect(await avlTree.searchUint(2)).to.be.equal(1); }); it("should handle searching for the non-existing key in the uint tree correctly", async () => { await avlTree.insertUintToUint(2, 1); - expect(await avlTree.searchUint(3)).to.be.false; + expect(await avlTree.searchUint(3)).to.be.equal(0); }); it("should handle searching for the zero key in the uint tree correctly", async () => { await avlTree.insertUintToUint(2, 1); - expect(await avlTree.searchUint(0)).to.be.false; + expect(await avlTree.searchUint(0)).to.be.equal(0); }); it("should get value for the existing node in the uint tree correctly", async () => { @@ -312,58 +302,21 @@ describe("AvlTree", () => { await avlTree.insertAddressToUint(3, USER1); await avlTree.insertAddressToUint(4, USER2); - await avlTree.insertStringToUint(5, "test1"); - await avlTree.insertStringToUint(6, "test2"); - - expect(await avlTree.getUintValueUint(1)).to.equal(4); - expect(await avlTree.getUintValueUint(2)).to.equal(2); + expect(await avlTree.getUintValueUint(1)).to.deep.equal([true, 4]); + expect(await avlTree.getUintValueUint(2)).to.deep.equal([true, 2]); + expect(await avlTree.getUintValueUint(5)).to.deep.equal([false, 0]); - expect(await avlTree.getAddressValueUint(3)).to.equal(USER1); - expect(await avlTree.getAddressValueUint(4)).to.equal(USER2); - - expect(await avlTree.getStringValueUint(5)).to.equal("test1"); - expect(await avlTree.getStringValueUint(6)).to.equal("test2"); + expect(await avlTree.getAddressValueUint(3)).to.deep.equal([true, USER1.address]); + expect(await avlTree.getAddressValueUint(4)).to.deep.equal([true, USER2.address]); + expect(await avlTree.getAddressValueUint(6)).to.deep.equal([false, ZERO_ADDR]); }); it("should handle getting value for the non-existing node in the uint tree correctly", async () => { - await expect(avlTree.getUintValueUint(1)).to.be.revertedWith("AvlTree: node with such key doesn't exist"); + expect(await avlTree.getUintValueUint(1)).to.deep.equal([false, 0]); await avlTree.insertUintToUint(1, 30); - await expect(avlTree.getUintValueUint(2)).to.be.revertedWith("AvlTree: node with such key doesn't exist"); - await expect(avlTree.getUintValueUint(0)).to.be.revertedWith("AvlTree: node with such key doesn't exist"); - }); - - it("should get minimum node in the uint tree correctly", async () => { - await avlTree.setUintValueComparatorUint(); - - await avlTree.insertUintToUint(4, 10); - await avlTree.insertUintToUint(5, 4); - await avlTree.insertUintToUint(1, 200); - await avlTree.insertUintToUint(2, 6); - - expect(await avlTree.getMinUint()).to.be.equal(5); - }); - - it("should get maximum node in the uint tree correctly", async () => { - await avlTree.insertUintToUint(4, 100); - await avlTree.insertUintToUint(5, 4); - await avlTree.insertUintToUint(8, 80); - await avlTree.insertUintToUint(1, 6); - - expect(await avlTree.getMaxUint()).to.be.equal(8); - }); - - it("should get the root key in the uint tree correctly", async () => { - expect(await avlTree.rootUint()).to.be.equal(0); - - await avlTree.insertUintToUint(1, 10); - await avlTree.insertUintToUint(100, 10); - await avlTree.insertUintToUint(22, 4); - await avlTree.insertUintToUint(7, 90); - await avlTree.insertUintToUint(2, 1); - await avlTree.insertUintToUint(3, 1); - - expect(await avlTree.rootUint()).to.be.equal(7); + expect(await avlTree.getUintValueUint(2)).to.deep.equal([false, 0]); + expect(await avlTree.getUintValueUint(0)).to.deep.equal([false, 0]); }); it("should get the treeSize correctly for the uint tree", async () => { @@ -381,45 +334,28 @@ describe("AvlTree", () => { it("should check if custom comparator is set for the uint tree correctly", async () => { expect(await avlTree.isCustomComparatorSetUint()).to.be.false; - await avlTree.setUintValueComparatorUint(); + await avlTree.setUintDescComparator(); expect(await avlTree.isCustomComparatorSetUint()).to.be.true; }); - }); - - it("should handle complex operations on the uint tree", async () => { - await avlTree.insertUintToUint(2, 10); - await avlTree.insertUintToUint(3, 1); - await avlTree.insertUintToUint(10, 18); - - await avlTree.removeUint(3); - await avlTree.insertUintToUint(4, 20); - await avlTree.insertUintToUint(5, 10); - - await avlTree.removeUint(2); - await avlTree.removeUint(10); - await avlTree.removeUint(4); - - await avlTree.insertUintToUint(4, 7); - await avlTree.insertUintToUint(1, 20); - await avlTree.insertUintToUint(2, 10); - await avlTree.insertUintToUint(20, 20); - await avlTree.insertUintToUint(111, 10); + it("should traverse the uint tree correctly", async () => { + await avlTree.insertUintToUint(2, 10); + await avlTree.insertUintToUint(4, 11); + await avlTree.insertUintToUint(1, 12); + await avlTree.insertUintToUint(6, 13); + await avlTree.insertUintToUint(7, 14); - await avlTree.removeUint(20); + let fullTraversal = await avlTree.traverseUint(); + expect(fullTraversal[0]).to.deep.equal([1, 2, 4, 6, 7]); + expect(fullTraversal[1]).to.deep.equal([12, 10, 11, 13, 14]); - await avlTree.insertUintToUint(16, 20); - await avlTree.insertUintToUint(17, 100); - await avlTree.insertUintToUint(18, 20); - await avlTree.insertUintToUint(19, 250); - await avlTree.insertUintToUint(20, 4); + let firstThreeTraversal = await avlTree.traverseFirstThreeUint(); + expect(firstThreeTraversal[0]).to.deep.equal([1, 2, 4]); + expect(firstThreeTraversal[1]).to.deep.equal([12, 10, 11]); - expect(await avlTree.rootUint()).to.equal(4); - expect(await avlTree.treeSizeUint()).to.equal(10); - expect(await avlTree.inOrderTraversalUint()).to.deep.equal([1, 2, 4, 5, 16, 17, 18, 19, 20, 111]); - expect(await avlTree.preOrderTraversalUint()).to.deep.equal([4, 1, 2, 18, 16, 5, 17, 20, 19, 111]); - expect(await avlTree.postOrderTraversalUint()).to.deep.equal([2, 1, 5, 17, 16, 19, 111, 20, 18, 4]); + await expect(avlTree.brokenTraversalUint()).to.be.revertedWith("Traversal: No more nodes"); + }); }); }); @@ -435,18 +371,17 @@ describe("AvlTree", () => { await avlTree.insertUintToBytes32(encodeBytes32String("5"), 2); expect(await avlTree.rootBytes32()).to.equal(encodeBytes32String("4")); - expect(await avlTree.getUintValueBytes32(encodeBytes32String("4"))).to.be.equal(22); + expect(await avlTree.getUintValueBytes32(encodeBytes32String("4"))).to.deep.equal([true, 22]); - expect(await avlTree.getMinBytes32()).to.equal(encodeBytes32String("6")); - expect(await avlTree.getUintValueBytes32(encodeBytes32String("6"))).to.be.equal(2); + expect(await avlTree.treeSizeBytes32()).to.equal(6); - expect(await avlTree.getMaxBytes32()).to.equal(encodeBytes32String("1")); - expect(await avlTree.getUintValueBytes32(encodeBytes32String("1"))).to.be.equal(12); + let fullTraversal = await avlTree.traverseBytes32(); + expect(fullTraversal[0]).to.deep.equal(uintToBytes32Array([6, 5, 4, 3, 2, 1])); + expect(fullTraversal[1]).to.deep.equal([2, 2, 22, 112, 20, 12]); - expect(await avlTree.treeSizeBytes32()).to.equal(6); - expect(await avlTree.inOrderTraversalBytes32()).to.deep.equal(uintToBytes32Array([6, 5, 4, 3, 2, 1])); - expect(await avlTree.preOrderTraversalBytes32()).to.deep.equal(uintToBytes32Array([4, 6, 5, 2, 3, 1])); - expect(await avlTree.postOrderTraversalBytes32()).to.deep.equal(uintToBytes32Array([5, 6, 3, 1, 2, 4])); + let firstThreeTraversal = await avlTree.traverseFirstThreeBytes32(); + expect(firstThreeTraversal[0]).to.deep.equal(uintToBytes32Array([6, 5, 4])); + expect(firstThreeTraversal[1]).to.deep.equal([2, 2, 22]); }); it("should remove nodes in the bytes32 tree correctly", async () => { @@ -460,10 +395,12 @@ describe("AvlTree", () => { expect(await avlTree.rootBytes32()).to.equal(encodeBytes32String("4")); expect(await avlTree.treeSizeBytes32()).to.equal(3); - expect(await avlTree.searchBytes32(encodeBytes32String("2"))).to.be.false; - expect(await avlTree.inOrderTraversalBytes32()).to.deep.equal(uintToBytes32Array([1, 4, 5])); - expect(await avlTree.preOrderTraversalBytes32()).to.deep.equal(uintToBytes32Array([4, 1, 5])); - expect(await avlTree.postOrderTraversalBytes32()).to.deep.equal(uintToBytes32Array([1, 5, 4])); + expect(await avlTree.searchBytes32(encodeBytes32String("2"))).to.be.equal(0); + expect(await avlTree.getUintValueBytes32(encodeBytes32String("2"))).to.deep.equal([false, 0]); + + let traversal = await avlTree.traverseBytes32(); + expect(traversal[0]).to.deep.equal(uintToBytes32Array([1, 4, 5])); + expect(traversal[1]).to.deep.equal([1, 6, 5]); await avlTree.insertUintToBytes32(encodeBytes32String("3"), 3); await avlTree.insertUintToBytes32(encodeBytes32String("6"), 4); @@ -475,13 +412,17 @@ describe("AvlTree", () => { expect(await avlTree.rootBytes32()).to.equal(encodeBytes32String("4")); expect(await avlTree.treeSizeBytes32()).to.equal(4); - expect(await avlTree.searchBytes32(encodeBytes32String("2"))).to.be.true; - expect(await avlTree.searchBytes32(encodeBytes32String("1"))).to.be.false; - expect(await avlTree.searchBytes32(encodeBytes32String("3"))).to.be.false; + expect(await avlTree.searchBytes32(encodeBytes32String("2"))).to.be.equal(7); + expect(await avlTree.searchBytes32(encodeBytes32String("1"))).to.be.equal(0); + expect(await avlTree.searchBytes32(encodeBytes32String("3"))).to.be.equal(0); + + expect(await avlTree.getUintValueBytes32(encodeBytes32String("2"))).to.deep.equal([true, 2]); + expect(await avlTree.getUintValueBytes32(encodeBytes32String("1"))).to.deep.equal([false, 0]); + expect(await avlTree.getUintValueBytes32(encodeBytes32String("3"))).to.deep.equal([false, 0]); - expect(await avlTree.inOrderTraversalBytes32()).to.deep.equal(uintToBytes32Array([2, 4, 5, 6])); - expect(await avlTree.preOrderTraversalBytes32()).to.deep.equal(uintToBytes32Array([4, 2, 5, 6])); - expect(await avlTree.postOrderTraversalBytes32()).to.deep.equal(uintToBytes32Array([2, 6, 5, 4])); + traversal = await avlTree.traverseBytes32(); + expect(traversal[0]).to.deep.equal(uintToBytes32Array([2, 4, 5, 6])); + expect(traversal[1]).to.deep.equal([2, 6, 5, 4]); await avlTree.removeBytes32(encodeBytes32String("2")); await avlTree.removeBytes32(encodeBytes32String("5")); @@ -493,13 +434,13 @@ describe("AvlTree", () => { }); it("should handle searching in the bytes32 tree correctly", async () => { - expect(await avlTree.searchBytes32(encodeBytes32String("1"))).to.be.false; + expect(await avlTree.searchBytes32(encodeBytes32String("1"))).to.be.equal(0); await avlTree.insertUintToBytes32(encodeBytes32String("1"), 18); - expect(await avlTree.searchBytes32(encodeBytes32String("1"))).to.be.true; - expect(await avlTree.searchBytes32(encodeBytes32String("2"))).to.be.false; - expect(await avlTree.searchBytes32(encodeBytes32String("0"))).to.be.false; + expect(await avlTree.searchBytes32(encodeBytes32String("1"))).to.be.equal(1); + expect(await avlTree.searchBytes32(encodeBytes32String("2"))).to.be.equal(0); + expect(await avlTree.searchBytes32(encodeBytes32String("0"))).to.be.equal(0); }); it("should check if custom comparator is set for the bytes32 tree correctly", async () => { @@ -513,118 +454,115 @@ describe("AvlTree", () => { describe("Address Tree", () => { it("should insert nodes to the address tree correctly", async () => { - await avlTree.setUintValueComparatorAddress(); + let users = [USER1.address, USER2.address, USER3.address, USER4.address, USER5.address, USER6.address]; + let addresses = users.map((user) => user); + + addresses.sort((a, b) => a.localeCompare(b)); + + await avlTree.insertUintToAddress(addresses[5], 10); + await avlTree.insertUintToAddress(addresses[3], 22); + await avlTree.insertUintToAddress(addresses[4], 15); + await avlTree.insertUintToAddress(addresses[2], 6); + await avlTree.insertUintToAddress(addresses[1], 16); + + expect(await avlTree.rootAddress()).to.equal(addresses[4]); + expect(await avlTree.getUintValueAddress(addresses[4])).to.deep.equal([true, 15]); - await avlTree.insertUintToAddress(USER1, 10); - await avlTree.insertUintToAddress(USER2, 22); - await avlTree.insertUintToAddress(USER3, 15); - await avlTree.insertUintToAddress(USER4, 6); - await avlTree.insertUintToAddress(USER5, 16); - await avlTree.insertUintToAddress(USER6, 17); + expect(await avlTree.treeSizeAddress()).to.equal(5); - expect(await avlTree.rootAddress()).to.equal(USER3); - expect(await avlTree.getUintValueAddress(USER3)).to.be.equal(15); + let traversal = await avlTree.traverseAddress(); + expect(traversal[0]).to.deep.equal([addresses[1], addresses[2], addresses[3], addresses[4], addresses[5]]); + expect(traversal[1]).to.deep.equal([16, 6, 22, 15, 10]); - expect(await avlTree.getMinAddress()).to.equal(USER4); - expect(await avlTree.getUintValueAddress(USER4)).to.be.equal(6); + await avlTree.insertUintToAddress(addresses[0], 17); - expect(await avlTree.getMaxAddress()).to.equal(USER2); - expect(await avlTree.getUintValueAddress(USER2)).to.be.equal(22); + expect(await avlTree.rootAddress()).to.equal(addresses[2]); + expect(await avlTree.getUintValueAddress(addresses[2])).to.deep.equal([true, 6]); expect(await avlTree.treeSizeAddress()).to.equal(6); - expect(await avlTree.inOrderTraversalAddress()).to.deep.equal([ - USER4.address, - USER1.address, - USER3.address, - USER5.address, - USER6.address, - USER2.address, - ]); - expect(await avlTree.preOrderTraversalAddress()).to.deep.equal([ - USER3.address, - USER1.address, - USER4.address, - USER6.address, - USER5.address, - USER2.address, - ]); - expect(await avlTree.postOrderTraversalAddress()).to.deep.equal([ - USER4.address, - USER1.address, - USER5.address, - USER2.address, - USER6.address, - USER3.address, + + traversal = await avlTree.traverseAddress(); + expect(traversal[0]).to.deep.equal([ + addresses[0], + addresses[1], + addresses[2], + addresses[3], + addresses[4], + addresses[5], ]); + expect(traversal[1]).to.deep.equal([17, 16, 6, 22, 15, 10]); + + const firstThreeTraversal = await avlTree.traverseFirstThreeAddress(); + expect(firstThreeTraversal[0]).to.deep.equal([addresses[0], addresses[1], addresses[2]]); + expect(firstThreeTraversal[1]).to.deep.equal([17, 16, 6]); }); it("should remove nodes in the address tree correctly", async () => { - await avlTree.setUintValueComparatorAddress(); + let users = [USER1.address, USER2.address, USER3.address, USER4.address, USER5.address, USER6.address]; + let addresses = users.map((user) => user); - await avlTree.insertUintToAddress(USER1, 2); - await avlTree.insertUintToAddress(USER2, 1); - await avlTree.insertUintToAddress(USER3, 5); - await avlTree.insertUintToAddress(USER4, 6); + addresses.sort((a, b) => b.localeCompare(a)); - await avlTree.removeAddress(USER1); + await avlTree.setAddressDescComparator(); + + await avlTree.insertUintToAddress(addresses[1], 2); + await avlTree.insertUintToAddress(addresses[0], 1); + await avlTree.insertUintToAddress(addresses[4], 5); + await avlTree.insertUintToAddress(addresses[5], 6); + + await avlTree.removeAddress(addresses[1]); - expect(await avlTree.rootAddress()).to.equal(USER3); + expect(await avlTree.rootAddress()).to.equal(addresses[4]); expect(await avlTree.treeSizeAddress()).to.equal(3); - expect(await avlTree.searchAddress(USER1)).to.be.false; - expect(await avlTree.inOrderTraversalAddress()).to.deep.equal([USER2.address, USER3.address, USER4.address]); - expect(await avlTree.preOrderTraversalAddress()).to.deep.equal([USER3.address, USER2.address, USER4.address]); - expect(await avlTree.postOrderTraversalAddress()).to.deep.equal([USER2.address, USER4.address, USER3.address]); + expect(await avlTree.searchAddress(addresses[1])).to.be.equal(0); - await avlTree.insertUintToAddress(USER5, 3); - await avlTree.insertUintToAddress(USER6, 4); - await avlTree.insertUintToAddress(USER1, 2); + let traversal = await avlTree.traverseAddress(); + expect(traversal[0]).to.deep.equal([addresses[0], addresses[4], addresses[5]]); + expect(traversal[1]).to.deep.equal([1, 5, 6]); + + await avlTree.insertUintToAddress(addresses[2], 3); + await avlTree.insertUintToAddress(addresses[3], 4); + await avlTree.insertUintToAddress(addresses[1], 2); - await avlTree.removeAddress(USER2); - await avlTree.removeAddress(USER5); + await avlTree.removeAddress(addresses[2]); + await avlTree.removeAddress(addresses[4]); - expect(await avlTree.rootAddress()).to.equal(USER6); + expect(await avlTree.rootAddress()).to.equal(addresses[3]); expect(await avlTree.treeSizeAddress()).to.equal(4); - expect(await avlTree.searchAddress(USER1)).to.be.true; - expect(await avlTree.searchAddress(USER2)).to.be.false; - expect(await avlTree.searchAddress(USER5)).to.be.false; + expect(await avlTree.searchAddress(addresses[0])).to.be.equal(2); + expect(await avlTree.searchAddress(addresses[2])).to.be.equal(0); + expect(await avlTree.searchAddress(addresses[4])).to.be.equal(0); - expect(await avlTree.inOrderTraversalAddress()).to.deep.equal([ - USER1.address, - USER6.address, - USER3.address, - USER4.address, - ]); - expect(await avlTree.preOrderTraversalAddress()).to.deep.equal([ - USER6.address, - USER1.address, - USER3.address, - USER4.address, - ]); - expect(await avlTree.postOrderTraversalAddress()).to.deep.equal([ - USER1.address, - USER4.address, - USER3.address, - USER6.address, - ]); + expect(await avlTree.getUintValueAddress(addresses[0])).to.deep.equal([true, 1]); + expect(await avlTree.getUintValueAddress(addresses[2])).to.deep.equal([false, 0]); + expect(await avlTree.getUintValueAddress(addresses[4])).to.deep.equal([false, 0]); - await avlTree.removeAddress(USER1); - await avlTree.removeAddress(USER3); - await avlTree.removeAddress(USER4); - await avlTree.removeAddress(USER6); + traversal = await avlTree.traverseAddress(); + expect(traversal[0]).to.deep.equal([addresses[0], addresses[1], addresses[3], addresses[5]]); + expect(traversal[1]).to.deep.equal([1, 2, 4, 6]); + + await avlTree.removeAddress(addresses[1]); + await avlTree.removeAddress(addresses[5]); + await avlTree.removeAddress(addresses[3]); + await avlTree.removeAddress(addresses[0]); expect(await avlTree.rootAddress()).to.equal(ZERO_ADDR); expect(await avlTree.treeSizeAddress()).to.equal(0); + + traversal = await avlTree.traverseAddress(); + expect(traversal[0].length).to.be.equal(0); + expect(traversal[1].length).to.be.equal(0); }); it("should handle searching in the address tree correctly", async () => { - expect(await avlTree.searchAddress(USER1)).to.be.false; + expect(await avlTree.searchAddress(USER1)).to.be.equal(0); await avlTree.insertUintToAddress(USER1, 2); - expect(await avlTree.searchAddress(USER1)).to.be.true; - expect(await avlTree.searchAddress(USER5)).to.be.false; - expect(await avlTree.searchAddress(ZERO_ADDR)).to.be.false; + expect(await avlTree.searchAddress(USER1)).to.be.equal(1); + expect(await avlTree.searchAddress(USER5)).to.be.equal(0); + expect(await avlTree.searchAddress(ZERO_ADDR)).to.be.equal(0); }); it("should check if custom comparator is set for the address tree correctly", async () => { From 7b90762fb2e07b6e8d36cc2a73dec8bad53f6a28 Mon Sep 17 00:00:00 2001 From: mllwchrry Date: Thu, 6 Jun 2024 13:51:07 +0300 Subject: [PATCH 05/11] insert and remove optimization --- contracts/libs/data-structures/AvlTree.sol | 168 +++++++--------- .../mock/libs/data-structures/AvlTreeMock.sol | 82 ++++---- test/libs/data-structures/AvlTree.test.ts | 185 ++++++++++-------- 3 files changed, 224 insertions(+), 211 deletions(-) diff --git a/contracts/libs/data-structures/AvlTree.sol b/contracts/libs/data-structures/AvlTree.sol index c17a7bab..fb629dcd 100644 --- a/contracts/libs/data-structures/AvlTree.sol +++ b/contracts/libs/data-structures/AvlTree.sol @@ -165,7 +165,7 @@ library AvlTree { */ function setComparator( UintAVL storage tree, - function(bytes32, bytes32) view returns (int8) comparator_ + function(bytes32, bytes32) view returns (int256) comparator_ ) internal { _setComparator(tree._tree, comparator_); } @@ -201,9 +201,8 @@ library AvlTree { * @param key_ the key of the node to search for. * @return True if the node exists, false otherwise. */ - function search(UintAVL storage tree, uint256 key_) internal view returns (uint64) { - return - _search(tree._tree.tree, tree._tree.root, bytes32(key_), _getComparator(tree._tree)); + function get(UintAVL storage tree, uint256 key_) internal view returns (bytes32) { + return _get(tree._tree, bytes32(key_)); } /** @@ -214,8 +213,8 @@ library AvlTree { * @param key_ the key to get the value for. * @return The value associated with the key. */ - function getValue(UintAVL storage tree, uint256 key_) internal view returns (bool, bytes32) { - return _getValue(tree._tree, bytes32(key_)); + function tryGet(UintAVL storage tree, uint256 key_) internal view returns (bool, bytes32) { + return _tryGet(tree._tree, bytes32(key_)); } /** @@ -223,18 +222,16 @@ library AvlTree { * @param tree self. * @return The size of the tree. */ - function treeSize(UintAVL storage tree) internal view returns (uint64) { - return uint64(_treeSize(tree._tree)); + function size(UintAVL storage tree) internal view returns (uint64) { + return uint64(_size(tree._tree)); } - function beginTraversal( - UintAVL storage tree - ) internal view returns (Traversal.Iterator memory) { - return _beginTraversal(tree._tree); + function first(UintAVL storage tree) internal view returns (Traversal.Iterator memory) { + return _first(tree._tree); } - function endTraversal(UintAVL storage tree) internal view returns (Traversal.Iterator memory) { - return _endTraversal(tree._tree); + function last(UintAVL storage tree) internal view returns (Traversal.Iterator memory) { + return _last(tree._tree); } /** @@ -263,7 +260,7 @@ library AvlTree { */ function setComparator( Bytes32AVL storage tree, - function(bytes32, bytes32) view returns (int8) comparator_ + function(bytes32, bytes32) view returns (int256) comparator_ ) internal { _setComparator(tree._tree, comparator_); } @@ -299,8 +296,8 @@ library AvlTree { * @param key_ the key of the node to search for. * @return True if the node exists, false otherwise. */ - function search(Bytes32AVL storage tree, bytes32 key_) internal view returns (uint64) { - return _search(tree._tree.tree, tree._tree.root, key_, _getComparator(tree._tree)); + function get(Bytes32AVL storage tree, bytes32 key_) internal view returns (bytes32) { + return _get(tree._tree, key_); } /** @@ -311,11 +308,8 @@ library AvlTree { * @param key_ the key to get the value for. * @return The value associated with the key. */ - function getValue( - Bytes32AVL storage tree, - bytes32 key_ - ) internal view returns (bool, bytes32) { - return _getValue(tree._tree, key_); + function tryGet(Bytes32AVL storage tree, bytes32 key_) internal view returns (bool, bytes32) { + return _tryGet(tree._tree, key_); } /** @@ -323,20 +317,16 @@ library AvlTree { * @param tree self. * @return The size of the tree. */ - function treeSize(Bytes32AVL storage tree) internal view returns (uint64) { - return uint64(_treeSize(tree._tree)); + function size(Bytes32AVL storage tree) internal view returns (uint64) { + return uint64(_size(tree._tree)); } - function beginTraversal( - Bytes32AVL storage tree - ) internal view returns (Traversal.Iterator memory) { - return _beginTraversal(tree._tree); + function first(Bytes32AVL storage tree) internal view returns (Traversal.Iterator memory) { + return _first(tree._tree); } - function endTraversal( - Bytes32AVL storage tree - ) internal view returns (Traversal.Iterator memory) { - return _endTraversal(tree._tree); + function last(Bytes32AVL storage tree) internal view returns (Traversal.Iterator memory) { + return _last(tree._tree); } /** @@ -365,7 +355,7 @@ library AvlTree { */ function setComparator( AddressAVL storage tree, - function(bytes32, bytes32) view returns (int8) comparator_ + function(bytes32, bytes32) view returns (int256) comparator_ ) internal { _setComparator(tree._tree, comparator_); } @@ -401,14 +391,8 @@ library AvlTree { * @param key_ the key of the node to search for. * @return True if the node exists, false otherwise. */ - function search(AddressAVL storage tree, address key_) internal view returns (uint64) { - return - _search( - tree._tree.tree, - tree._tree.root, - bytes32(uint256(uint160(key_))), - _getComparator(tree._tree) - ); + function get(AddressAVL storage tree, address key_) internal view returns (bytes32) { + return _get(tree._tree, bytes32(uint256(uint160(key_)))); } /** @@ -419,11 +403,8 @@ library AvlTree { * @param key_ the key to get the value for. * @return The value associated with the key. */ - function getValue( - AddressAVL storage tree, - address key_ - ) internal view returns (bool, bytes32) { - return _getValue(tree._tree, bytes32(uint256(uint160(key_)))); + function tryGet(AddressAVL storage tree, address key_) internal view returns (bool, bytes32) { + return _tryGet(tree._tree, bytes32(uint256(uint160(key_)))); } /** @@ -431,20 +412,16 @@ library AvlTree { * @param tree self. * @return The size of the tree. */ - function treeSize(AddressAVL storage tree) internal view returns (uint64) { - return uint64(_treeSize(tree._tree)); + function size(AddressAVL storage tree) internal view returns (uint64) { + return uint64(_size(tree._tree)); } - function beginTraversal( - AddressAVL storage tree - ) internal view returns (Traversal.Iterator memory) { - return _beginTraversal(tree._tree); + function first(AddressAVL storage tree) internal view returns (Traversal.Iterator memory) { + return _first(tree._tree); } - function endTraversal( - AddressAVL storage tree - ) internal view returns (Traversal.Iterator memory) { - return _endTraversal(tree._tree); + function last(AddressAVL storage tree) internal view returns (Traversal.Iterator memory) { + return _last(tree._tree); } /** @@ -477,14 +454,14 @@ library AvlTree { uint64 removedCount; bool isCustomComparatorSet; mapping(uint64 => Node) tree; - function(bytes32, bytes32) view returns (int8) comparator; + function(bytes32, bytes32) view returns (int256) comparator; } function _setComparator( Tree storage tree, - function(bytes32, bytes32) view returns (int8) comparator_ + function(bytes32, bytes32) view returns (int256) comparator_ ) private { - require(_treeSize(tree) == 0, "AvlTree: the tree must be empty"); + require(_size(tree) == 0, "AvlTree: the tree must be empty"); tree.isCustomComparatorSet = true; @@ -493,10 +470,6 @@ library AvlTree { function _insert(Tree storage tree, bytes32 key_, bytes32 value_) private { require(key_ != 0, "AvlTree: key is not allowed to be 0"); - require( - _search(tree.tree, tree.root, key_, _getComparator(tree)) == 0, - "AvlTree: the node already exists" - ); tree.totalCount++; @@ -513,10 +486,6 @@ library AvlTree { function _remove(Tree storage tree, bytes32 key_) private { require(key_ != 0, "AvlTree: key is not allowed to be 0"); - require( - _search(tree.tree, tree.root, key_, _getComparator(tree)) != 0, - "AvlTree: the node doesn't exist" - ); tree.root = _removeNode(tree.tree, tree.root, 0, bytes32(key_), _getComparator(tree)); @@ -530,8 +499,10 @@ library AvlTree { uint64 parent_, bytes32 key_, bytes32 value_, - function(bytes32, bytes32) view returns (int8) comparator_ + function(bytes32, bytes32) view returns (int256) comparator_ ) private returns (uint64) { + int256 comparison_ = comparator_(key_, _tree[node_].key); + if (_tree[node_].key == 0) { _tree[index_] = Node({ key: key_, @@ -545,7 +516,7 @@ library AvlTree { return index_; } - if (comparator_(key_, _tree[node_].key) <= 0) { + if (comparison_ < 0) { _tree[node_].left = _insertNode( _tree, index_, @@ -555,6 +526,8 @@ library AvlTree { value_, comparator_ ); + } else if (comparison_ == 0) { + revert("AvlTree: the node already exists"); } else { _tree[node_].right = _insertNode( _tree, @@ -575,9 +548,11 @@ library AvlTree { uint64 node_, uint64 parent_, bytes32 key_, - function(bytes32, bytes32) view returns (int8) comparator_ + function(bytes32, bytes32) view returns (int256) comparator_ ) private returns (uint64) { - int8 comparison_ = comparator_(key_, _tree[node_].key); + require(node_ != 0, "AvlTree: the node doesn't exist"); + + int256 comparison_ = comparator_(key_, _tree[node_].key); if (comparison_ == 0) { uint64 left_ = _tree[node_].left; @@ -633,7 +608,7 @@ library AvlTree { return _balance(_tree, node_); } - function _rotateLeft( + function _rotateRight( mapping(uint64 => Node) storage _tree, uint64 node_ ) private returns (uint64) { @@ -657,7 +632,7 @@ library AvlTree { return temp_; } - function _rotateRight( + function _rotateLeft( mapping(uint64 => Node) storage _tree, uint64 node_ ) private returns (uint64) { @@ -692,16 +667,16 @@ library AvlTree { if (_left.height > _right.height + 1) { if (_tree[_left.right].height > _tree[_left.left].height) { - _tree[node_].left = _rotateRight(_tree, _tree[node_].left); + _tree[node_].left = _rotateLeft(_tree, _tree[node_].left); } - return _rotateLeft(_tree, node_); + return _rotateRight(_tree, node_); } else if (_right.height > _left.height + 1) { if (_tree[_right.left].height > _tree[_right.right].height) { - _tree[node_].right = _rotateLeft(_tree, _tree[node_].right); + _tree[node_].right = _rotateRight(_tree, _tree[node_].right); } - return _rotateRight(_tree, node_); + return _rotateLeft(_tree, node_); } return node_; @@ -717,13 +692,13 @@ library AvlTree { mapping(uint64 => Node) storage _tree, uint64 node_, bytes32 key_, - function(bytes32, bytes32) view returns (int8) comparator_ + function(bytes32, bytes32) view returns (int256) comparator_ ) private view returns (uint64) { if (node_ == 0) { return 0; } - int8 comparison_ = comparator_(key_, _tree[node_].key); + int256 comparison_ = comparator_(key_, _tree[node_].key); if (comparison_ == 0) { return node_; @@ -734,7 +709,15 @@ library AvlTree { } } - function _getValue(Tree storage tree, bytes32 key_) private view returns (bool, bytes32) { + function _get(Tree storage tree, bytes32 key_) private view returns (bytes32) { + uint64 index_ = _search(tree.tree, tree.root, key_, _getComparator(tree)); + + require(index_ != 0, "AvlTree: the node doesn't exist"); + + return tree.tree[index_].value; + } + + function _tryGet(Tree storage tree, bytes32 key_) private view returns (bool, bytes32) { uint64 index_ = _search(tree.tree, tree.root, key_, _getComparator(tree)); if (index_ == 0) { @@ -744,23 +727,17 @@ library AvlTree { return (true, tree.tree[index_].value); } - function _treeSize(Tree storage tree) private view returns (uint256) { + function _size(Tree storage tree) private view returns (uint256) { return tree.totalCount - tree.removedCount; } - function _beginTraversal(Tree storage tree) private view returns (Traversal.Iterator memory) { + function _first(Tree storage tree) private view returns (Traversal.Iterator memory) { uint256 treeMappingSlot_; assembly { treeMappingSlot_ := add(tree.slot, 1) } - uint64 root_ = tree.root; - - if (root_ == 0) { - return Traversal.Iterator({treeMappingSlot: treeMappingSlot_, currentNode: 0}); - } - - uint64 current_ = root_; + uint64 current_ = tree.root; while (tree.tree[current_].left != 0) { current_ = tree.tree[current_].left; } @@ -768,22 +745,27 @@ library AvlTree { return Traversal.Iterator({treeMappingSlot: treeMappingSlot_, currentNode: current_}); } - function _endTraversal(Tree storage tree) private pure returns (Traversal.Iterator memory) { + function _last(Tree storage tree) private view returns (Traversal.Iterator memory) { uint256 treeMappingSlot_; assembly { treeMappingSlot_ := add(tree.slot, 1) } - return Traversal.Iterator({treeMappingSlot: treeMappingSlot_, currentNode: 0}); + uint64 current_ = tree.root; + while (tree.tree[current_].right != 0) { + current_ = tree.tree[current_].right; + } + + return Traversal.Iterator({treeMappingSlot: treeMappingSlot_, currentNode: current_}); } function _getComparator( Tree storage tree - ) private view returns (function(bytes32, bytes32) view returns (int8)) { + ) private view returns (function(bytes32, bytes32) view returns (int256)) { return tree.isCustomComparatorSet ? tree.comparator : _defaultComparator; } - function _defaultComparator(bytes32 key1_, bytes32 key2_) private pure returns (int8) { + function _defaultComparator(bytes32 key1_, bytes32 key2_) private pure returns (int256) { if (key1_ < key2_) { return -1; } diff --git a/contracts/mock/libs/data-structures/AvlTreeMock.sol b/contracts/mock/libs/data-structures/AvlTreeMock.sol index 53ce6e08..dd4ce62b 100644 --- a/contracts/mock/libs/data-structures/AvlTreeMock.sol +++ b/contracts/mock/libs/data-structures/AvlTreeMock.sol @@ -51,52 +51,56 @@ contract AvlTreeMock { _addressTree.remove(key_); } - function searchUint(uint256 key_) external view returns (uint64) { - return _uintTree.search(key_); + function getUintFromUint(uint256 key_) external view returns (uint256) { + return uint256(_uintTree.get(key_)); } - function searchBytes32(bytes32 key_) external view returns (uint64) { - return _bytes32Tree.search(key_); + function getAddressFromUint(uint256 key_) external view returns (address) { + return address(uint160(uint256(_uintTree.get(key_)))); } - function searchAddress(address key_) external view returns (uint64) { - return _addressTree.search(key_); + function getUintFromBytes32(bytes32 key_) external view returns (uint256) { + return uint256(_bytes32Tree.get(key_)); } - function getUintValueUint(uint256 key_) external view returns (bool, uint256) { - (bool exists_, bytes32 value_) = _uintTree.getValue(key_); + function getUintFromAddress(address key_) external view returns (uint256) { + return uint256(_addressTree.get(key_)); + } + + function tryGetUintFromUint(uint256 key_) external view returns (bool, uint256) { + (bool exists_, bytes32 value_) = _uintTree.tryGet(key_); return (exists_, uint256(value_)); } - function getAddressValueUint(uint256 key_) external view returns (bool, address) { - (bool exists_, bytes32 value_) = _uintTree.getValue(key_); + function tryGetAddressFromUint(uint256 key_) external view returns (bool, address) { + (bool exists_, bytes32 value_) = _uintTree.tryGet(key_); return (exists_, address(uint160(uint256(value_)))); } - function getUintValueBytes32(bytes32 key_) external view returns (bool, uint256) { - (bool exists_, bytes32 value_) = _bytes32Tree.getValue(key_); + function tryGetUintFromBytes32(bytes32 key_) external view returns (bool, uint256) { + (bool exists_, bytes32 value_) = _bytes32Tree.tryGet(key_); return (exists_, uint256(value_)); } - function getUintValueAddress(address key_) external view returns (bool, uint256) { - (bool exists_, bytes32 value_) = _addressTree.getValue(key_); + function tryGetUintFromAddress(address key_) external view returns (bool, uint256) { + (bool exists_, bytes32 value_) = _addressTree.tryGet(key_); return (exists_, uint256(value_)); } - function treeSizeUint() external view returns (uint64) { - return _uintTree.treeSize(); + function sizeUint() external view returns (uint64) { + return _uintTree.size(); } - function treeSizeBytes32() external view returns (uint64) { - return _bytes32Tree.treeSize(); + function sizeBytes32() external view returns (uint64) { + return _bytes32Tree.size(); } - function treeSizeAddress() external view returns (uint64) { - return _addressTree.treeSize(); + function sizeAddress() external view returns (uint64) { + return _addressTree.size(); } function rootUint() external view returns (uint256) { @@ -112,10 +116,10 @@ contract AvlTreeMock { } function traverseUint() external view returns (uint256[] memory, uint256[] memory) { - Traversal.Iterator memory iterator_ = _uintTree.beginTraversal(); + Traversal.Iterator memory iterator_ = _uintTree.first(); - uint256[] memory keys_ = new uint256[](_uintTree.treeSize()); - uint256[] memory values_ = new uint256[](_uintTree.treeSize()); + uint256[] memory keys_ = new uint256[](_uintTree.size()); + uint256[] memory values_ = new uint256[](_uintTree.size()); if (keys_.length != 0) { (bytes32 bytesKey_, bytes32 bytesValue_) = iterator_.value(); @@ -139,7 +143,7 @@ contract AvlTreeMock { } function traverseFirstThreeUint() external view returns (uint256[] memory, uint256[] memory) { - Traversal.Iterator memory iterator_ = _uintTree.beginTraversal(); + Traversal.Iterator memory iterator_ = _uintTree.first(); uint256[] memory keys_ = new uint256[](3); uint256[] memory values_ = new uint256[](3); @@ -160,7 +164,7 @@ contract AvlTreeMock { values_[index_] = uint256(bytesValue_); if (index_++ == 2) { - iterator_ = _uintTree.endTraversal(); + iterator_ = _uintTree.last(); } } @@ -168,10 +172,10 @@ contract AvlTreeMock { } function brokenTraversalUint() external view { - Traversal.Iterator memory iterator_ = _uintTree.beginTraversal(); + Traversal.Iterator memory iterator_ = _uintTree.first(); - uint256[] memory keys_ = new uint256[](_uintTree.treeSize()); - uint256[] memory values_ = new uint256[](_uintTree.treeSize()); + uint256[] memory keys_ = new uint256[](_uintTree.size()); + uint256[] memory values_ = new uint256[](_uintTree.size()); (bytes32 bytesKey_, bytes32 bytesValue_) = iterator_.value(); @@ -191,10 +195,10 @@ contract AvlTreeMock { } function traverseBytes32() external view returns (bytes32[] memory, bytes32[] memory) { - Traversal.Iterator memory iterator_ = _bytes32Tree.beginTraversal(); + Traversal.Iterator memory iterator_ = _bytes32Tree.first(); - bytes32[] memory keys_ = new bytes32[](_bytes32Tree.treeSize()); - bytes32[] memory values_ = new bytes32[](_bytes32Tree.treeSize()); + bytes32[] memory keys_ = new bytes32[](_bytes32Tree.size()); + bytes32[] memory values_ = new bytes32[](_bytes32Tree.size()); if (keys_.length != 0) { (keys_[0], values_[0]) = iterator_.value(); @@ -216,7 +220,7 @@ contract AvlTreeMock { view returns (bytes32[] memory, bytes32[] memory) { - Traversal.Iterator memory iterator_ = _bytes32Tree.beginTraversal(); + Traversal.Iterator memory iterator_ = _bytes32Tree.first(); bytes32[] memory keys_ = new bytes32[](3); bytes32[] memory values_ = new bytes32[](3); @@ -231,7 +235,7 @@ contract AvlTreeMock { (keys_[index_], values_[index_]) = iterator_.next(); if (index_++ == 2) { - iterator_ = _bytes32Tree.endTraversal(); + iterator_ = _bytes32Tree.last(); } } @@ -239,10 +243,10 @@ contract AvlTreeMock { } function traverseAddress() external view returns (address[] memory, address[] memory) { - Traversal.Iterator memory iterator_ = _addressTree.beginTraversal(); + Traversal.Iterator memory iterator_ = _addressTree.first(); - address[] memory keys_ = new address[](_addressTree.treeSize()); - address[] memory values_ = new address[](_addressTree.treeSize()); + address[] memory keys_ = new address[](_addressTree.size()); + address[] memory values_ = new address[](_addressTree.size()); if (keys_.length != 0) { (bytes32 bytesKey_, bytes32 bytesValue_) = iterator_.value(); @@ -270,7 +274,7 @@ contract AvlTreeMock { view returns (address[] memory, address[] memory) { - Traversal.Iterator memory iterator_ = _addressTree.beginTraversal(); + Traversal.Iterator memory iterator_ = _addressTree.first(); address[] memory keys_ = new address[](3); address[] memory values_ = new address[](3); @@ -291,7 +295,7 @@ contract AvlTreeMock { values_[index_] = address(uint160(uint256(bytesValue_))); if (index_++ == 2) { - iterator_ = _addressTree.endTraversal(); + iterator_ = _addressTree.last(); } } @@ -310,7 +314,7 @@ contract AvlTreeMock { return _addressTree.isCustomComparatorSet(); } - function _descComparator(bytes32 key1_, bytes32 key2_) private pure returns (int8) { + function _descComparator(bytes32 key1_, bytes32 key2_) private pure returns (int256) { if (key1_ > key2_) { return -1; } diff --git a/test/libs/data-structures/AvlTree.test.ts b/test/libs/data-structures/AvlTree.test.ts index 2b4b124f..5a557830 100644 --- a/test/libs/data-structures/AvlTree.test.ts +++ b/test/libs/data-structures/AvlTree.test.ts @@ -47,7 +47,7 @@ describe("AvlTree", () => { await avlTree.insertUintToUint(1, 3); expect(await avlTree.rootUint()).to.equal(4); - expect(await avlTree.treeSizeUint()).to.equal(3); + expect(await avlTree.sizeUint()).to.equal(3); let traversal = await avlTree.traverseUint(); expect(traversal[0]).to.deep.equal([1, 4, 12]); @@ -58,7 +58,7 @@ describe("AvlTree", () => { await avlTree.insertUintToUint(5, 6); expect(await avlTree.rootUint()).to.equal(10); - expect(await avlTree.treeSizeUint()).to.equal(6); + expect(await avlTree.sizeUint()).to.equal(6); traversal = await avlTree.traverseUint(); expect(traversal[0]).to.deep.equal([1, 4, 5, 10, 12, 200]); @@ -67,7 +67,7 @@ describe("AvlTree", () => { await avlTree.insertUintToUint(15, 7); expect(await avlTree.rootUint()).to.equal(10); - expect(await avlTree.treeSizeUint()).to.equal(7); + expect(await avlTree.sizeUint()).to.equal(7); traversal = await avlTree.traverseUint(); expect(traversal[0]).to.deep.equal([1, 4, 5, 10, 12, 15, 200]); @@ -77,7 +77,7 @@ describe("AvlTree", () => { await avlTree.insertUintToUint(3, 9); expect(await avlTree.rootUint()).to.equal(10); - expect(await avlTree.treeSizeUint()).to.equal(9); + expect(await avlTree.sizeUint()).to.equal(9); traversal = await avlTree.traverseUint(); expect(traversal[0]).to.deep.equal([1, 2, 3, 4, 5, 10, 12, 15, 200]); @@ -90,7 +90,7 @@ describe("AvlTree", () => { await avlTree.insertAddressToUint(4, USER2); expect(await avlTree.rootUint()).to.equal(4); - expect(await avlTree.treeSizeUint()).to.equal(3); + expect(await avlTree.sizeUint()).to.equal(3); let traversal = await avlTree.traverseUint(); expect(traversal[0]).to.deep.equal([2, 4, 6]); @@ -102,7 +102,7 @@ describe("AvlTree", () => { await avlTree.insertAddressToUint(5, USER3); expect(await avlTree.rootUint()).to.equal(4); - expect(await avlTree.treeSizeUint()).to.equal(7); + expect(await avlTree.sizeUint()).to.equal(7); traversal = await avlTree.traverseUint(); expect(traversal[0]).to.deep.equal([1, 2, 4, 5, 6, 7, 13]); @@ -146,7 +146,7 @@ describe("AvlTree", () => { await avlTree.insertUintToUint(20, 4); expect(await avlTree.rootUint()).to.equal(4); - expect(await avlTree.treeSizeUint()).to.equal(10); + expect(await avlTree.sizeUint()).to.equal(10); const traversal = await avlTree.traverseUint(); expect(traversal[0]).to.deep.equal([1, 2, 4, 5, 16, 17, 18, 19, 20, 111]); @@ -174,12 +174,12 @@ describe("AvlTree", () => { await avlTree.insertUintToUint(10, 10); expect(await avlTree.rootUint()).to.equal(3); - expect(await avlTree.treeSizeUint()).to.equal(7); + expect(await avlTree.sizeUint()).to.equal(7); await avlTree.removeUint(3); expect(await avlTree.rootUint()).to.equal(4); - expect(await avlTree.treeSizeUint()).to.equal(6); + expect(await avlTree.sizeUint()).to.equal(6); const traversal = await avlTree.traverseUint(); expect(traversal[0]).to.deep.equal([1, 2, 4, 6, 7, 10]); @@ -201,7 +201,7 @@ describe("AvlTree", () => { await avlTree.removeUint(5); expect(await avlTree.rootUint()).to.equal(6); - expect(await avlTree.treeSizeUint()).to.equal(5); + expect(await avlTree.sizeUint()).to.equal(5); const traversal = await avlTree.traverseUint(); expect(traversal[0]).to.deep.equal([1, 4, 6, 7, 9]); @@ -221,7 +221,7 @@ describe("AvlTree", () => { await avlTree.removeUint(3); expect(await avlTree.rootUint()).to.be.equal(0); - expect(await avlTree.treeSizeUint()).to.be.equal(0); + expect(await avlTree.sizeUint()).to.be.equal(0); const traversal = await avlTree.traverseUint(); expect(traversal[0].length).to.be.equal(0); @@ -261,7 +261,7 @@ describe("AvlTree", () => { await avlTree.insertUintToUint(5, 1); expect(await avlTree.rootUint()).to.equal(4); - expect(await avlTree.treeSizeUint()).to.equal(6); + expect(await avlTree.sizeUint()).to.equal(6); const traversal = await avlTree.traverseUint(); expect(traversal[0]).to.deep.equal([6, 5, 4, 3, 2, 1]); @@ -276,25 +276,6 @@ describe("AvlTree", () => { }); describe("getters", () => { - it("should handle searching for the existing key in the uint tree correctly", async () => { - await avlTree.insertUintToUint(2, 20); - await avlTree.insertUintToUint(1, 10); - - expect(await avlTree.searchUint(2)).to.be.equal(1); - }); - - it("should handle searching for the non-existing key in the uint tree correctly", async () => { - await avlTree.insertUintToUint(2, 1); - - expect(await avlTree.searchUint(3)).to.be.equal(0); - }); - - it("should handle searching for the zero key in the uint tree correctly", async () => { - await avlTree.insertUintToUint(2, 1); - - expect(await avlTree.searchUint(0)).to.be.equal(0); - }); - it("should get value for the existing node in the uint tree correctly", async () => { await avlTree.insertUintToUint(1, 4); await avlTree.insertUintToUint(2, 2); @@ -302,25 +283,40 @@ describe("AvlTree", () => { await avlTree.insertAddressToUint(3, USER1); await avlTree.insertAddressToUint(4, USER2); - expect(await avlTree.getUintValueUint(1)).to.deep.equal([true, 4]); - expect(await avlTree.getUintValueUint(2)).to.deep.equal([true, 2]); - expect(await avlTree.getUintValueUint(5)).to.deep.equal([false, 0]); + expect(await avlTree.getUintFromUint(1)).to.be.equal(4); + expect(await avlTree.tryGetUintFromUint(1)).to.deep.equal([true, 4]); - expect(await avlTree.getAddressValueUint(3)).to.deep.equal([true, USER1.address]); - expect(await avlTree.getAddressValueUint(4)).to.deep.equal([true, USER2.address]); - expect(await avlTree.getAddressValueUint(6)).to.deep.equal([false, ZERO_ADDR]); + expect(await avlTree.getUintFromUint(2)).to.deep.equal(2); + expect(await avlTree.tryGetUintFromUint(2)).to.deep.equal([true, 2]); + + await expect(avlTree.getUintFromUint(5)).to.be.revertedWith("AvlTree: the node doesn't exist"); + expect(await avlTree.tryGetUintFromUint(5)).to.deep.equal([false, 0]); + + expect(await avlTree.getAddressFromUint(3)).to.be.equal(USER1.address); + expect(await avlTree.tryGetAddressFromUint(3)).to.deep.equal([true, USER1.address]); + + expect(await avlTree.getAddressFromUint(4)).to.be.equal(USER2.address); + expect(await avlTree.tryGetAddressFromUint(4)).to.deep.equal([true, USER2.address]); + + await expect(avlTree.getAddressFromUint(6)).to.be.revertedWith("AvlTree: the node doesn't exist"); + expect(await avlTree.tryGetAddressFromUint(6)).to.deep.equal([false, ZERO_ADDR]); }); it("should handle getting value for the non-existing node in the uint tree correctly", async () => { - expect(await avlTree.getUintValueUint(1)).to.deep.equal([false, 0]); + await expect(avlTree.getUintFromUint(1)).to.be.revertedWith("AvlTree: the node doesn't exist"); + expect(await avlTree.tryGetUintFromUint(1)).to.deep.equal([false, 0]); await avlTree.insertUintToUint(1, 30); - expect(await avlTree.getUintValueUint(2)).to.deep.equal([false, 0]); - expect(await avlTree.getUintValueUint(0)).to.deep.equal([false, 0]); + + await expect(avlTree.getUintFromUint(2)).to.be.revertedWith("AvlTree: the node doesn't exist"); + expect(await avlTree.tryGetUintFromUint(2)).to.deep.equal([false, 0]); + + await expect(avlTree.getUintFromUint(0)).to.be.revertedWith("AvlTree: the node doesn't exist"); + expect(await avlTree.tryGetUintFromUint(0)).to.deep.equal([false, 0]); }); it("should get the treeSize correctly for the uint tree", async () => { - expect(await avlTree.treeSizeUint()).to.be.equal(0); + expect(await avlTree.sizeUint()).to.be.equal(0); await avlTree.insertUintToUint(1, 10); await avlTree.insertUintToUint(2, 10); @@ -328,7 +324,7 @@ describe("AvlTree", () => { await avlTree.insertUintToUint(3, 4); await avlTree.insertUintToUint(7, 90); - expect(await avlTree.treeSizeUint()).to.be.equal(3); + expect(await avlTree.sizeUint()).to.be.equal(3); }); it("should check if custom comparator is set for the uint tree correctly", async () => { @@ -371,9 +367,10 @@ describe("AvlTree", () => { await avlTree.insertUintToBytes32(encodeBytes32String("5"), 2); expect(await avlTree.rootBytes32()).to.equal(encodeBytes32String("4")); - expect(await avlTree.getUintValueBytes32(encodeBytes32String("4"))).to.deep.equal([true, 22]); + expect(await avlTree.getUintFromBytes32(encodeBytes32String("4"))).to.be.equal(22); + expect(await avlTree.tryGetUintFromBytes32(encodeBytes32String("4"))).to.deep.equal([true, 22]); - expect(await avlTree.treeSizeBytes32()).to.equal(6); + expect(await avlTree.sizeBytes32()).to.equal(6); let fullTraversal = await avlTree.traverseBytes32(); expect(fullTraversal[0]).to.deep.equal(uintToBytes32Array([6, 5, 4, 3, 2, 1])); @@ -394,9 +391,11 @@ describe("AvlTree", () => { expect(await avlTree.rootBytes32()).to.equal(encodeBytes32String("4")); - expect(await avlTree.treeSizeBytes32()).to.equal(3); - expect(await avlTree.searchBytes32(encodeBytes32String("2"))).to.be.equal(0); - expect(await avlTree.getUintValueBytes32(encodeBytes32String("2"))).to.deep.equal([false, 0]); + expect(await avlTree.sizeBytes32()).to.equal(3); + await expect(avlTree.getUintFromBytes32(encodeBytes32String("2"))).to.be.revertedWith( + "AvlTree: the node doesn't exist", + ); + expect(await avlTree.tryGetUintFromBytes32(encodeBytes32String("2"))).to.deep.equal([false, 0]); let traversal = await avlTree.traverseBytes32(); expect(traversal[0]).to.deep.equal(uintToBytes32Array([1, 4, 5])); @@ -410,15 +409,20 @@ describe("AvlTree", () => { await avlTree.removeBytes32(encodeBytes32String("3")); expect(await avlTree.rootBytes32()).to.equal(encodeBytes32String("4")); - expect(await avlTree.treeSizeBytes32()).to.equal(4); + expect(await avlTree.sizeBytes32()).to.equal(4); + + expect(await avlTree.getUintFromBytes32(encodeBytes32String("2"))).to.be.equal(2); + expect(await avlTree.tryGetUintFromBytes32(encodeBytes32String("2"))).to.deep.equal([true, 2]); - expect(await avlTree.searchBytes32(encodeBytes32String("2"))).to.be.equal(7); - expect(await avlTree.searchBytes32(encodeBytes32String("1"))).to.be.equal(0); - expect(await avlTree.searchBytes32(encodeBytes32String("3"))).to.be.equal(0); + await expect(avlTree.getUintFromBytes32(encodeBytes32String("1"))).to.be.revertedWith( + "AvlTree: the node doesn't exist", + ); + expect(await avlTree.tryGetUintFromBytes32(encodeBytes32String("1"))).to.deep.equal([false, 0]); - expect(await avlTree.getUintValueBytes32(encodeBytes32String("2"))).to.deep.equal([true, 2]); - expect(await avlTree.getUintValueBytes32(encodeBytes32String("1"))).to.deep.equal([false, 0]); - expect(await avlTree.getUintValueBytes32(encodeBytes32String("3"))).to.deep.equal([false, 0]); + await expect(avlTree.getUintFromBytes32(encodeBytes32String("3"))).to.be.revertedWith( + "AvlTree: the node doesn't exist", + ); + expect(await avlTree.tryGetUintFromBytes32(encodeBytes32String("3"))).to.deep.equal([false, 0]); traversal = await avlTree.traverseBytes32(); expect(traversal[0]).to.deep.equal(uintToBytes32Array([2, 4, 5, 6])); @@ -430,17 +434,29 @@ describe("AvlTree", () => { await avlTree.removeBytes32(encodeBytes32String("4")); expect(await avlTree.rootBytes32()).to.equal(encodeBytes32String("")); - expect(await avlTree.treeSizeBytes32()).to.equal(0); + expect(await avlTree.sizeBytes32()).to.equal(0); }); - it("should handle searching in the bytes32 tree correctly", async () => { - expect(await avlTree.searchBytes32(encodeBytes32String("1"))).to.be.equal(0); + it("should handle getting value in the bytes32 tree correctly", async () => { + await expect(avlTree.getUintFromBytes32(encodeBytes32String("1"))).to.be.revertedWith( + "AvlTree: the node doesn't exist", + ); + expect(await avlTree.tryGetUintFromBytes32(encodeBytes32String("1"))).to.deep.equal([false, 0]); await avlTree.insertUintToBytes32(encodeBytes32String("1"), 18); - expect(await avlTree.searchBytes32(encodeBytes32String("1"))).to.be.equal(1); - expect(await avlTree.searchBytes32(encodeBytes32String("2"))).to.be.equal(0); - expect(await avlTree.searchBytes32(encodeBytes32String("0"))).to.be.equal(0); + expect(await avlTree.getUintFromBytes32(encodeBytes32String("1"))).to.be.equal(18); + expect(await avlTree.tryGetUintFromBytes32(encodeBytes32String("1"))).to.deep.equal([true, 18]); + + await expect(avlTree.getUintFromBytes32(encodeBytes32String("2"))).to.be.revertedWith( + "AvlTree: the node doesn't exist", + ); + expect(await avlTree.tryGetUintFromBytes32(encodeBytes32String("2"))).to.deep.equal([false, 0]); + + await expect(avlTree.getUintFromBytes32(encodeBytes32String("0"))).to.be.revertedWith( + "AvlTree: the node doesn't exist", + ); + expect(await avlTree.tryGetUintFromBytes32(encodeBytes32String("0"))).to.deep.equal([false, 0]); }); it("should check if custom comparator is set for the bytes32 tree correctly", async () => { @@ -466,9 +482,10 @@ describe("AvlTree", () => { await avlTree.insertUintToAddress(addresses[1], 16); expect(await avlTree.rootAddress()).to.equal(addresses[4]); - expect(await avlTree.getUintValueAddress(addresses[4])).to.deep.equal([true, 15]); + expect(await avlTree.getUintFromAddress(addresses[4])).to.be.equal(15); + expect(await avlTree.tryGetUintFromAddress(addresses[4])).to.deep.equal([true, 15]); - expect(await avlTree.treeSizeAddress()).to.equal(5); + expect(await avlTree.sizeAddress()).to.equal(5); let traversal = await avlTree.traverseAddress(); expect(traversal[0]).to.deep.equal([addresses[1], addresses[2], addresses[3], addresses[4], addresses[5]]); @@ -477,9 +494,10 @@ describe("AvlTree", () => { await avlTree.insertUintToAddress(addresses[0], 17); expect(await avlTree.rootAddress()).to.equal(addresses[2]); - expect(await avlTree.getUintValueAddress(addresses[2])).to.deep.equal([true, 6]); + expect(await avlTree.getUintFromAddress(addresses[2])).to.be.equal(6); + expect(await avlTree.tryGetUintFromAddress(addresses[2])).to.deep.equal([true, 6]); - expect(await avlTree.treeSizeAddress()).to.equal(6); + expect(await avlTree.sizeAddress()).to.equal(6); traversal = await avlTree.traverseAddress(); expect(traversal[0]).to.deep.equal([ @@ -513,8 +531,10 @@ describe("AvlTree", () => { await avlTree.removeAddress(addresses[1]); expect(await avlTree.rootAddress()).to.equal(addresses[4]); - expect(await avlTree.treeSizeAddress()).to.equal(3); - expect(await avlTree.searchAddress(addresses[1])).to.be.equal(0); + expect(await avlTree.sizeAddress()).to.equal(3); + + await expect(avlTree.getUintFromAddress(addresses[1])).to.be.revertedWith("AvlTree: the node doesn't exist"); + expect(await avlTree.tryGetUintFromAddress(addresses[1])).to.deep.equal([false, 0]); let traversal = await avlTree.traverseAddress(); expect(traversal[0]).to.deep.equal([addresses[0], addresses[4], addresses[5]]); @@ -528,15 +548,16 @@ describe("AvlTree", () => { await avlTree.removeAddress(addresses[4]); expect(await avlTree.rootAddress()).to.equal(addresses[3]); - expect(await avlTree.treeSizeAddress()).to.equal(4); + expect(await avlTree.sizeAddress()).to.equal(4); - expect(await avlTree.searchAddress(addresses[0])).to.be.equal(2); - expect(await avlTree.searchAddress(addresses[2])).to.be.equal(0); - expect(await avlTree.searchAddress(addresses[4])).to.be.equal(0); + expect(await avlTree.getUintFromAddress(addresses[0])).to.be.equal(1); + expect(await avlTree.tryGetUintFromAddress(addresses[0])).to.deep.equal([true, 1]); - expect(await avlTree.getUintValueAddress(addresses[0])).to.deep.equal([true, 1]); - expect(await avlTree.getUintValueAddress(addresses[2])).to.deep.equal([false, 0]); - expect(await avlTree.getUintValueAddress(addresses[4])).to.deep.equal([false, 0]); + await expect(avlTree.getUintFromAddress(addresses[2])).to.be.revertedWith("AvlTree: the node doesn't exist"); + expect(await avlTree.tryGetUintFromAddress(addresses[2])).to.deep.equal([false, 0]); + + await expect(avlTree.getUintFromAddress(addresses[4])).to.be.revertedWith("AvlTree: the node doesn't exist"); + expect(await avlTree.tryGetUintFromAddress(addresses[4])).to.deep.equal([false, 0]); traversal = await avlTree.traverseAddress(); expect(traversal[0]).to.deep.equal([addresses[0], addresses[1], addresses[3], addresses[5]]); @@ -548,21 +569,27 @@ describe("AvlTree", () => { await avlTree.removeAddress(addresses[0]); expect(await avlTree.rootAddress()).to.equal(ZERO_ADDR); - expect(await avlTree.treeSizeAddress()).to.equal(0); + expect(await avlTree.sizeAddress()).to.equal(0); traversal = await avlTree.traverseAddress(); expect(traversal[0].length).to.be.equal(0); expect(traversal[1].length).to.be.equal(0); }); - it("should handle searching in the address tree correctly", async () => { - expect(await avlTree.searchAddress(USER1)).to.be.equal(0); + it("should handle getting value in the address tree correctly", async () => { + await expect(avlTree.getUintFromAddress(USER1)).to.be.revertedWith("AvlTree: the node doesn't exist"); + expect(await avlTree.tryGetUintFromAddress(USER1)).to.deep.equal([false, 0]); await avlTree.insertUintToAddress(USER1, 2); - expect(await avlTree.searchAddress(USER1)).to.be.equal(1); - expect(await avlTree.searchAddress(USER5)).to.be.equal(0); - expect(await avlTree.searchAddress(ZERO_ADDR)).to.be.equal(0); + expect(await avlTree.getUintFromAddress(USER1)).to.be.equal(2); + expect(await avlTree.tryGetUintFromAddress(USER1)).to.deep.equal([true, 2]); + + await expect(avlTree.getUintFromAddress(USER5)).to.be.revertedWith("AvlTree: the node doesn't exist"); + expect(await avlTree.tryGetUintFromAddress(USER5)).to.deep.equal([false, 0]); + + await expect(avlTree.getUintFromAddress(ZERO_ADDR)).to.be.revertedWith("AvlTree: the node doesn't exist"); + expect(await avlTree.tryGetUintFromAddress(ZERO_ADDR)).to.deep.equal([false, 0]); }); it("should check if custom comparator is set for the address tree correctly", async () => { From bd486e33185f9eace6baeec9b40abbbb125e80dc Mon Sep 17 00:00:00 2001 From: mllwchrry Date: Fri, 7 Jun 2024 11:10:09 +0300 Subject: [PATCH 06/11] added natspec, hasPrev and prev --- contracts/libs/data-structures/AvlTree.sol | 411 +++++++++------ .../mock/libs/data-structures/AvlTreeMock.sol | 145 ++--- test/libs/data-structures/AvlTree.test.ts | 496 ++++++++---------- 3 files changed, 576 insertions(+), 476 deletions(-) diff --git a/contracts/libs/data-structures/AvlTree.sol b/contracts/libs/data-structures/AvlTree.sol index fb629dcd..30618752 100644 --- a/contracts/libs/data-structures/AvlTree.sol +++ b/contracts/libs/data-structures/AvlTree.sol @@ -5,112 +5,13 @@ import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; import {TypeCaster} from "../utils/TypeCaster.sol"; -library Traversal { - struct Iterator { - uint256 treeMappingSlot; - uint64 currentNode; - } - - function hasNext(Iterator memory iterator_) internal view returns (bool) { - AvlTree.Node memory node_ = _getNode(iterator_.treeMappingSlot, iterator_.currentNode); - - if (node_.right != 0) { - return true; - } - - uint64 currentNodeIndex_ = iterator_.currentNode; - - while (currentNodeIndex_ != 0) { - AvlTree.Node memory parent_ = _getNode(iterator_.treeMappingSlot, node_.parent); - - if (currentNodeIndex_ == parent_.left) { - return true; - } - - currentNodeIndex_ = node_.parent; - node_ = parent_; - } - - return false; - } - - function next(Iterator memory iterator_) internal view returns (bytes32, bytes32) { - uint64 currentNodeIndex_ = iterator_.currentNode; - - require(currentNodeIndex_ != 0, "Traversal: No more nodes"); - - AvlTree.Node memory node_ = _getNode(iterator_.treeMappingSlot, currentNodeIndex_); - - if (node_.right != 0) { - currentNodeIndex_ = node_.right; - - AvlTree.Node memory childNode_ = _getNode( - iterator_.treeMappingSlot, - currentNodeIndex_ - ); - - while (childNode_.left != 0) { - currentNodeIndex_ = childNode_.left; - childNode_ = _getNode(iterator_.treeMappingSlot, currentNodeIndex_); - } - } else { - uint64 parentIndex_ = node_.parent; - - AvlTree.Node memory parentNode_ = _getNode(iterator_.treeMappingSlot, parentIndex_); - - while (parentIndex_ != 0 && currentNodeIndex_ == parentNode_.right) { - currentNodeIndex_ = parentIndex_; - - parentIndex_ = parentNode_.parent; - parentNode_ = _getNode(iterator_.treeMappingSlot, parentIndex_); - } - - currentNodeIndex_ = parentIndex_; - } - - iterator_.currentNode = currentNodeIndex_; - - return value(iterator_); - } - - function value(Iterator memory iterator_) internal view returns (bytes32, bytes32) { - AvlTree.Node memory node_ = _getNode(iterator_.treeMappingSlot, iterator_.currentNode); - - return (node_.key, node_.value); - } - - function _getNode( - uint256 slot_, - uint64 index_ - ) private view returns (AvlTree.Node memory node_) { - bytes32 baseSlot_ = keccak256(abi.encode(index_, slot_)); - - assembly { - let valueSlot_ := add(baseSlot_, 1) - let packedSlot_ := add(baseSlot_, 2) - - let packedData_ := sload(packedSlot_) - - mstore(node_, sload(baseSlot_)) - mstore(add(node_, 0x20), sload(valueSlot_)) - mstore(add(node_, 0x40), and(packedData_, 0xFFFFFFFFFFFFFFFF)) - mstore(add(node_, 0x60), and(shr(64, packedData_), 0xFFFFFFFFFFFFFFFF)) - mstore(add(node_, 0x80), and(shr(128, packedData_), 0xFFFFFFFFFFFFFFFF)) - mstore(add(node_, 0xa0), and(shr(192, packedData_), 0xFFFFFFFFFFFFFFFF)) - } - - return node_; - } -} - /** * @notice AVL Tree module * * This library provides implementation of three sets with dynamic key types: * `UintAVL`, `Bytes32AVL` and `Bytes32AVL`. * - * Each element in the tree contains a bytes `value` field to allow storing different types - * of values including structs + * Each element in the tree contains a bytes32 `value` field to allow storing different types of values * * The implementation supports setting custom comparator function * @@ -118,10 +19,10 @@ library Traversal { * * | Statistic | _insert | _remove | * | --------- | ------------ | ---------------- | - * | count | 1000 | 1000 | - * | mean | 309,851 gas | 164,735 gas | - * | min | 162,211 gas | 48,691 gas | - * | max | 340,416 gas | 220,653 gas | + * | count | 5000 | 5000 | + * | mean | 222,578 gas | 115,744 gas | + * | min | 110,520 gas | 34,461 gas | + * | max | 263,275 gas | 171,815 gas | * * ## Usage example: * @@ -134,15 +35,13 @@ library Traversal { * * uintTree.setComparator(comparatorFunction); * - * uintTree.insert(1, abi.encode(1234)); - * - * uintTree.remove(1); + * uintTree.insert(1, bytes32(1234)); * - * uintTree.root(); + * uintTree.tryGet(1); * - * uintTree.treeSize(); + * uintTree.remove(1); * - * uintTree.inOrderTraversal(); + * uintTree.size(); * ``` */ library AvlTree { @@ -161,7 +60,7 @@ library AvlTree { /** * @notice The function to set a custom comparator function, that will be used to build the uint256 tree. * @param tree self. - * @param comparator_ The function that accepts keys and values of the nodes to compare. + * @param comparator_ The function that accepts keys of the nodes to compare. */ function setComparator( UintAVL storage tree, @@ -178,8 +77,8 @@ library AvlTree { * @param key_ the key to insert. * @param value_ the value to insert. */ - function insert(UintAVL storage tree, uint256 key_, bytes32 value_) internal { - _insert(tree._tree, bytes32(key_), value_); + function insert(UintAVL storage tree, bytes32 key_, uint256 value_) internal { + _insert(tree._tree, key_, bytes32(value_)); } /** @@ -189,32 +88,39 @@ library AvlTree { * @param tree self. * @param key_ the key of the node to remove. */ - function remove(UintAVL storage tree, uint256 key_) internal { - _remove(tree._tree, bytes32(key_)); + function remove(UintAVL storage tree, bytes32 key_) internal { + _remove(tree._tree, key_); } /** - * @notice The function to search for a node in the uint256 tree. - * Complexity is O(1). + * @notice The function to retrieve the value associated with a key in the uint256 tree. + * Complexity is O(log(n)), where n is the number of elements in the tree. + * + * Note: Reverts if the node with the specified key doesn't exist. * * @param tree self. - * @param key_ the key of the node to search for. - * @return True if the node exists, false otherwise. + * @param key_ the key to retrieve the value for. + * @return The value associated with the key. */ - function get(UintAVL storage tree, uint256 key_) internal view returns (bytes32) { - return _get(tree._tree, bytes32(key_)); + function get(UintAVL storage tree, bytes32 key_) internal view returns (uint256) { + return uint256(_get(tree._tree, key_)); } /** - * @notice The function to retrieve the value associated with a key in the uint256 tree. - * Complexity is O(1). + * @notice The function to try to retrieve the value associated with a key in the uint256 tree. + * Complexity is O(log(n)), where n is the number of elements in the tree. + * + * @dev Does not revert if the node with the specified key doesn't exist. * * @param tree self. - * @param key_ the key to get the value for. + * @param key_ the key of the node to try to retrieve the value for. + * @return True if the node with the specified key exists, false otherwise. * @return The value associated with the key. */ - function tryGet(UintAVL storage tree, uint256 key_) internal view returns (bool, bytes32) { - return _tryGet(tree._tree, bytes32(key_)); + function tryGet(UintAVL storage tree, bytes32 key_) internal view returns (bool, uint256) { + (bool exists_, bytes32 value_) = _tryGet(tree._tree, key_); + + return (exists_, uint256(value_)); } /** @@ -226,10 +132,22 @@ library AvlTree { return uint64(_size(tree._tree)); } + /** + * @notice The function to get the iterator pointing to the first (leftmost) node in the uint256 tree. + * @dev The functions can be utilized for an in-order traversal of the tree. + * @param tree self. + * @return The iterator pointing to the first node. + */ function first(UintAVL storage tree) internal view returns (Traversal.Iterator memory) { return _first(tree._tree); } + /** + * @notice The function to get the iterator pointing to the last (rightmost) node in the uint256 tree. + * @dev The functions can be utilized for an in-order backwards traversal of the tree. + * @param tree self. + * @return The iterator pointing to the last node. + */ function last(UintAVL storage tree) internal view returns (Traversal.Iterator memory) { return _last(tree._tree); } @@ -289,23 +207,28 @@ library AvlTree { } /** - * @notice The function to search for a node in the bytes32 tree. - * Complexity is O(1). + * @notice The function to retrieve the value associated with a key in the bytes32 tree. + * Complexity is O(log(n)), where n is the number of elements in the tree. + * + * Note: Reverts if the node with the specified key doesn't exist. * * @param tree self. - * @param key_ the key of the node to search for. - * @return True if the node exists, false otherwise. + * @param key_ the key to retrieve the value for. + * @return The value associated with the key. */ function get(Bytes32AVL storage tree, bytes32 key_) internal view returns (bytes32) { return _get(tree._tree, key_); } /** - * @notice The function to retrieve the value associated with a key in the bytes32 tree. - * Complexity is O(1). + * @notice The function to try to retrieve the value associated with a key in the bytes32 tree. + * Complexity is O(log(n)), where n is the number of elements in the tree. + * + * @dev Does not revert if the node with the specified key doesn't exist. * * @param tree self. - * @param key_ the key to get the value for. + * @param key_ the key of the node to try to retrieve the value for. + * @return True if the node with the specified key exists, false otherwise. * @return The value associated with the key. */ function tryGet(Bytes32AVL storage tree, bytes32 key_) internal view returns (bool, bytes32) { @@ -321,10 +244,22 @@ library AvlTree { return uint64(_size(tree._tree)); } + /** + * @notice The function to get the iterator pointing to the first (leftmost) node in the bytes32 tree. + * @dev The functions can be utilized for an in-order traversal of the tree. + * @param tree self. + * @return The iterator pointing to the first node. + */ function first(Bytes32AVL storage tree) internal view returns (Traversal.Iterator memory) { return _first(tree._tree); } + /** + * @notice The function to get the iterator pointing to the last (rightmost) node in the bytes32 tree. + * @dev The functions can be utilized for an in-order backwards traversal of the tree. + * @param tree self. + * @return The iterator pointing to the last node. + */ function last(Bytes32AVL storage tree) internal view returns (Traversal.Iterator memory) { return _last(tree._tree); } @@ -368,8 +303,8 @@ library AvlTree { * @param key_ The key to insert. * @param value_ The value to insert. */ - function insert(AddressAVL storage tree, address key_, bytes32 value_) internal { - _insert(tree._tree, bytes32(uint256(uint160(key_))), value_); + function insert(AddressAVL storage tree, bytes32 key_, address value_) internal { + _insert(tree._tree, key_, bytes32(uint256(uint160(value_)))); } /** @@ -379,32 +314,39 @@ library AvlTree { * @param tree self. * @param key_ the key of the node to remove. */ - function remove(AddressAVL storage tree, address key_) internal { - _remove(tree._tree, bytes32(uint256(uint160(key_)))); + function remove(AddressAVL storage tree, bytes32 key_) internal { + _remove(tree._tree, key_); } /** - * @notice The function to search for a node in the address tree. - * Complexity is O(1). + * @notice The function to retrieve the value associated with a key in the address tree. + * Complexity is O(log(n)), where n is the number of elements in the tree. + * + * Note: Reverts if the node with the specified key doesn't exist. * * @param tree self. - * @param key_ the key of the node to search for. - * @return True if the node exists, false otherwise. + * @param key_ the key to retrieve the value for. + * @return The value associated with the key. */ - function get(AddressAVL storage tree, address key_) internal view returns (bytes32) { - return _get(tree._tree, bytes32(uint256(uint160(key_)))); + function get(AddressAVL storage tree, bytes32 key_) internal view returns (address) { + return address(uint160(uint256(_get(tree._tree, key_)))); } /** - * @notice The function to retrieve the value associated with a key in the address tree. - * Complexity is O(1). + * @notice The function to try to retrieve the value associated with a key in the address tree. + * Complexity is O(log(n)), where n is the number of elements in the tree. + * + * @dev Does not revert if the node with the specified key doesn't exist. * * @param tree self. - * @param key_ the key to get the value for. + * @param key_ the key of the node to try to retrieve the value for. + * @return True if the node with the specified key exists, false otherwise. * @return The value associated with the key. */ - function tryGet(AddressAVL storage tree, address key_) internal view returns (bool, bytes32) { - return _tryGet(tree._tree, bytes32(uint256(uint160(key_)))); + function tryGet(AddressAVL storage tree, bytes32 key_) internal view returns (bool, address) { + (bool exists_, bytes32 value_) = _tryGet(tree._tree, key_); + + return (exists_, address(uint160(uint256(value_)))); } /** @@ -416,10 +358,22 @@ library AvlTree { return uint64(_size(tree._tree)); } + /** + * @notice The function to get the iterator pointing to the first (leftmost) node in the address tree. + * @dev The functions can be utilized for an in-order traversal of the tree. + * @param tree self. + * @return The iterator pointing to the first node. + */ function first(AddressAVL storage tree) internal view returns (Traversal.Iterator memory) { return _first(tree._tree); } + /** + * @notice The function to get the iterator pointing to the last (rightmost) node in the address tree. + * @dev The functions can be utilized for an in-order backwards traversal of the tree. + * @param tree self. + * @return The iterator pointing to the last node. + */ function last(AddressAVL storage tree) internal view returns (Traversal.Iterator memory) { return _last(tree._tree); } @@ -579,6 +533,8 @@ library AvlTree { _tree[left_].parent = temp_; } + _tree[temp_].parent = parent_; + return _balance(_tree, temp_); } else if (comparison_ < 0) { _tree[node_].left = _removeNode(_tree, _tree[node_].left, node_, key_, comparator_); @@ -777,3 +733,162 @@ library AvlTree { return 0; } } + +/** + * @notice Traversal module + * + * This library provides functions to perform an in-order traversal of the AVL Tree + */ +library Traversal { + /** + * @notice Iterator struct to keep track of the current position in the tree. + * @param treeMappingSlot The storage slot of the tree mapping. + * @param currentNode The index of the current node in the traversal. + */ + struct Iterator { + uint256 treeMappingSlot; + uint64 currentNode; + } + + /** + * @notice The function to check if there is a next node in the traversal. + * @param iterator_ self. + * @return True if there is a next node, false otherwise. + */ + function hasNext(Iterator memory iterator_) internal view returns (bool) { + return _has(iterator_, true); + } + + /** + * @notice The function to check if there is a previous node in the traversal. + * @param iterator_ self. + * @return True if there is a previous node, false otherwise. + */ + function hasPrev(Iterator memory iterator_) internal view returns (bool) { + return _has(iterator_, false); + } + + /** + * @notice The function to move the iterator to the next node and retrieve its key and value. + * @param iterator_ self. + * @return The key of the next node. + * @return The value of the next node. + */ + function next(Iterator memory iterator_) internal view returns (bytes32, bytes32) { + return _moveToAdjacent(iterator_, true); + } + + /** + * @notice The function to move the iterator to the previous node and retrieve its key and value. + * @param iterator_ self. + * @return The key of the previous node. + * @return The value of the previous node. + */ + function prev(Iterator memory iterator_) internal view returns (bytes32, bytes32) { + return _moveToAdjacent(iterator_, false); + } + + /** + * @notice The function to retrieve the key and value of the current node. + * @param iterator_ self. + * @return The key of the current node. + * @return The value of the current node. + */ + function value(Iterator memory iterator_) internal view returns (bytes32, bytes32) { + AvlTree.Node memory node_ = _getNode(iterator_.treeMappingSlot, iterator_.currentNode); + + return (node_.key, node_.value); + } + + function _has(Iterator memory iterator_, bool next_) private view returns (bool) { + AvlTree.Node memory node_ = _getNode(iterator_.treeMappingSlot, iterator_.currentNode); + + if (_adjacent(node_, next_) != 0) { + return true; + } + + uint64 currentNodeIndex_ = iterator_.currentNode; + + while (currentNodeIndex_ != 0) { + AvlTree.Node memory parent_ = _getNode(iterator_.treeMappingSlot, node_.parent); + + if (currentNodeIndex_ == _adjacent(parent_, !next_)) { + return true; + } + + currentNodeIndex_ = node_.parent; + node_ = parent_; + } + + return false; + } + + function _moveToAdjacent( + Iterator memory iterator_, + bool next_ + ) internal view returns (bytes32, bytes32) { + uint64 currentNodeIndex_ = iterator_.currentNode; + + require(currentNodeIndex_ != 0, "Traversal: no more nodes"); + + AvlTree.Node memory node_ = _getNode(iterator_.treeMappingSlot, currentNodeIndex_); + + if (_adjacent(node_, next_) != 0) { + currentNodeIndex_ = _adjacent(node_, next_); + + AvlTree.Node memory childNode_ = _getNode( + iterator_.treeMappingSlot, + currentNodeIndex_ + ); + + while (_adjacent(childNode_, !next_) != 0) { + currentNodeIndex_ = _adjacent(childNode_, !next_); + childNode_ = _getNode(iterator_.treeMappingSlot, currentNodeIndex_); + } + } else { + uint64 parentIndex_ = node_.parent; + + AvlTree.Node memory parentNode_ = _getNode(iterator_.treeMappingSlot, parentIndex_); + + while (parentIndex_ != 0 && currentNodeIndex_ == _adjacent(parentNode_, next_)) { + currentNodeIndex_ = parentIndex_; + + parentIndex_ = parentNode_.parent; + parentNode_ = _getNode(iterator_.treeMappingSlot, parentIndex_); + } + + currentNodeIndex_ = parentIndex_; + } + + iterator_.currentNode = currentNodeIndex_; + + return value(iterator_); + } + + function _adjacent(AvlTree.Node memory node_, bool next_) private pure returns (uint64) { + return next_ ? node_.right : node_.left; + } + + function _getNode( + uint256 slot_, + uint64 index_ + ) private view returns (AvlTree.Node memory node_) { + bytes32 baseSlot_ = keccak256(abi.encode(index_, slot_)); + + assembly { + let valueSlot_ := add(baseSlot_, 1) + let packedSlot_ := add(baseSlot_, 2) + + let packedData_ := sload(packedSlot_) + + mstore(node_, sload(baseSlot_)) + mstore(add(node_, 0x20), sload(valueSlot_)) + mstore(add(node_, 0x40), and(packedData_, 0xFFFFFFFFFFFFFFFF)) + mstore(add(node_, 0x60), and(shr(64, packedData_), 0xFFFFFFFFFFFFFFFF)) + mstore(add(node_, 0x80), and(shr(128, packedData_), 0xFFFFFFFFFFFFFFFF)) + mstore(add(node_, 0xa0), and(shr(192, packedData_), 0xFFFFFFFFFFFFFFFF)) + } + + return node_; + } +} diff --git a/contracts/mock/libs/data-structures/AvlTreeMock.sol b/contracts/mock/libs/data-structures/AvlTreeMock.sol index dd4ce62b..78f44b7c 100644 --- a/contracts/mock/libs/data-structures/AvlTreeMock.sol +++ b/contracts/mock/libs/data-structures/AvlTreeMock.sol @@ -23,72 +23,52 @@ contract AvlTreeMock { _addressTree.setComparator(_descComparator); } - function insertUintToUint(uint256 key_, uint256 value_) external { - _uintTree.insert(key_, bytes32(value_)); + function insertUint(uint256 key_, uint256 value_) external { + _uintTree.insert(bytes32(key_), value_); } - function insertAddressToUint(uint256 key_, address value_) external { - _uintTree.insert(key_, bytes32(uint256(uint160(value_)))); + function insertBytes32(uint256 key_, bytes32 value_) external { + _bytes32Tree.insert(bytes32(key_), value_); } - function insertUintToBytes32(bytes32 key_, uint256 value_) external { - _bytes32Tree.insert(key_, bytes32(value_)); - } - - function insertUintToAddress(address key_, uint256 value_) external { - _addressTree.insert(key_, bytes32(value_)); + function insertAddress(uint256 key_, address value_) external { + _addressTree.insert(bytes32(key_), value_); } function removeUint(uint256 key_) external { - _uintTree.remove(key_); + _uintTree.remove(bytes32(key_)); } - function removeBytes32(bytes32 key_) external { - _bytes32Tree.remove(key_); + function removeBytes32(uint256 key_) external { + _bytes32Tree.remove(bytes32(key_)); } - function removeAddress(address key_) external { - _addressTree.remove(key_); + function removeAddress(uint256 key_) external { + _addressTree.remove(bytes32(key_)); } - function getUintFromUint(uint256 key_) external view returns (uint256) { - return uint256(_uintTree.get(key_)); + function getUint(uint256 key_) external view returns (uint256) { + return _uintTree.get(bytes32(key_)); } - function getAddressFromUint(uint256 key_) external view returns (address) { - return address(uint160(uint256(_uintTree.get(key_)))); + function getBytes32(uint256 key_) external view returns (bytes32) { + return _bytes32Tree.get(bytes32(key_)); } - function getUintFromBytes32(bytes32 key_) external view returns (uint256) { - return uint256(_bytes32Tree.get(key_)); + function getAddressValue(uint256 key_) external view returns (address) { + return _addressTree.get(bytes32(key_)); } - function getUintFromAddress(address key_) external view returns (uint256) { - return uint256(_addressTree.get(key_)); + function tryGetUint(uint256 key_) external view returns (bool, uint256) { + return _uintTree.tryGet(bytes32(key_)); } - function tryGetUintFromUint(uint256 key_) external view returns (bool, uint256) { - (bool exists_, bytes32 value_) = _uintTree.tryGet(key_); - - return (exists_, uint256(value_)); + function tryGetBytes32(uint256 key_) external view returns (bool, bytes32) { + return _bytes32Tree.tryGet(bytes32(key_)); } - function tryGetAddressFromUint(uint256 key_) external view returns (bool, address) { - (bool exists_, bytes32 value_) = _uintTree.tryGet(key_); - - return (exists_, address(uint160(uint256(value_)))); - } - - function tryGetUintFromBytes32(bytes32 key_) external view returns (bool, uint256) { - (bool exists_, bytes32 value_) = _bytes32Tree.tryGet(key_); - - return (exists_, uint256(value_)); - } - - function tryGetUintFromAddress(address key_) external view returns (bool, uint256) { - (bool exists_, bytes32 value_) = _addressTree.tryGet(key_); - - return (exists_, uint256(value_)); + function tryGetAddress(uint256 key_) external view returns (bool, address) { + return _addressTree.tryGet(bytes32(key_)); } function sizeUint() external view returns (uint64) { @@ -107,12 +87,12 @@ contract AvlTreeMock { return uint256(_uintTree._tree.tree[_uintTree._tree.root].key); } - function rootBytes32() external view returns (bytes32) { - return _bytes32Tree._tree.tree[_bytes32Tree._tree.root].key; + function rootBytes32() external view returns (uint256) { + return uint256(_bytes32Tree._tree.tree[_bytes32Tree._tree.root].key); } - function rootAddress() external view returns (address) { - return address(uint160(uint256(_addressTree._tree.tree[_addressTree._tree.root].key))); + function rootAddress() external view returns (uint256) { + return uint256(_addressTree._tree.tree[_addressTree._tree.root].key); } function traverseUint() external view returns (uint256[] memory, uint256[] memory) { @@ -194,20 +174,53 @@ contract AvlTreeMock { } } - function traverseBytes32() external view returns (bytes32[] memory, bytes32[] memory) { + function backwardsTraversalUint() external view returns (uint256[] memory, uint256[] memory) { + Traversal.Iterator memory iterator_ = _uintTree.last(); + + uint256[] memory keys_ = new uint256[](_uintTree.size()); + uint256[] memory values_ = new uint256[](_uintTree.size()); + + if (keys_.length != 0) { + (bytes32 bytesKey_, bytes32 bytesValue_) = iterator_.value(); + + keys_[0] = uint256(bytesKey_); + values_[0] = uint256(bytesValue_); + } + + uint256 index_ = 1; + + while (iterator_.hasPrev()) { + (bytes32 bytesKey_, bytes32 bytesValue_) = iterator_.prev(); + + keys_[index_] = uint256(bytesKey_); + values_[index_] = uint256(bytesValue_); + + index_++; + } + + return (keys_, values_); + } + + function traverseBytes32() external view returns (uint256[] memory, bytes32[] memory) { Traversal.Iterator memory iterator_ = _bytes32Tree.first(); - bytes32[] memory keys_ = new bytes32[](_bytes32Tree.size()); + uint256[] memory keys_ = new uint256[](_bytes32Tree.size()); bytes32[] memory values_ = new bytes32[](_bytes32Tree.size()); if (keys_.length != 0) { - (keys_[0], values_[0]) = iterator_.value(); + (bytes32 bytesKey_, bytes32 bytesValue_) = iterator_.value(); + + keys_[0] = uint256(bytesKey_); + values_[0] = bytesValue_; } uint256 index_ = 1; while (iterator_.hasNext()) { - (keys_[index_], values_[index_]) = iterator_.next(); + (bytes32 bytesKey_, bytes32 bytesValue_) = iterator_.next(); + + keys_[index_] = uint256(bytesKey_); + values_[index_] = bytesValue_; index_++; } @@ -218,21 +231,27 @@ contract AvlTreeMock { function traverseFirstThreeBytes32() external view - returns (bytes32[] memory, bytes32[] memory) + returns (uint256[] memory, bytes32[] memory) { Traversal.Iterator memory iterator_ = _bytes32Tree.first(); - bytes32[] memory keys_ = new bytes32[](3); + uint256[] memory keys_ = new uint256[](3); bytes32[] memory values_ = new bytes32[](3); if (keys_.length != 0) { - (keys_[0], values_[0]) = iterator_.value(); + (bytes32 bytesKey_, bytes32 bytesValue_) = iterator_.value(); + + keys_[0] = uint256(bytesKey_); + values_[0] = bytesValue_; } uint256 index_ = 1; while (iterator_.hasNext()) { - (keys_[index_], values_[index_]) = iterator_.next(); + (bytes32 bytesKey_, bytes32 bytesValue_) = iterator_.next(); + + keys_[index_] = uint256(bytesKey_); + values_[index_] = bytesValue_; if (index_++ == 2) { iterator_ = _bytes32Tree.last(); @@ -242,16 +261,16 @@ contract AvlTreeMock { return (keys_, values_); } - function traverseAddress() external view returns (address[] memory, address[] memory) { + function traverseAddress() external view returns (uint256[] memory, address[] memory) { Traversal.Iterator memory iterator_ = _addressTree.first(); - address[] memory keys_ = new address[](_addressTree.size()); + uint256[] memory keys_ = new uint256[](_addressTree.size()); address[] memory values_ = new address[](_addressTree.size()); if (keys_.length != 0) { (bytes32 bytesKey_, bytes32 bytesValue_) = iterator_.value(); - keys_[0] = address(uint160(uint256(bytesKey_))); + keys_[0] = uint256(bytesKey_); values_[0] = address(uint160(uint256(bytesValue_))); } @@ -260,7 +279,7 @@ contract AvlTreeMock { while (iterator_.hasNext()) { (bytes32 bytesKey_, bytes32 bytesValue_) = iterator_.next(); - keys_[index_] = address(uint160(uint256(bytesKey_))); + keys_[index_] = uint256(bytesKey_); values_[index_] = address(uint160(uint256(bytesValue_))); index_++; @@ -272,17 +291,17 @@ contract AvlTreeMock { function traverseFirstThreeAddress() external view - returns (address[] memory, address[] memory) + returns (uint256[] memory, address[] memory) { Traversal.Iterator memory iterator_ = _addressTree.first(); - address[] memory keys_ = new address[](3); + uint256[] memory keys_ = new uint256[](3); address[] memory values_ = new address[](3); if (keys_.length != 0) { (bytes32 bytesKey_, bytes32 bytesValue_) = iterator_.value(); - keys_[0] = address(uint160(uint256(bytesKey_))); + keys_[0] = uint256(bytesKey_); values_[0] = address(uint160(uint256(bytesValue_))); } @@ -291,7 +310,7 @@ contract AvlTreeMock { while (iterator_.hasNext()) { (bytes32 bytesKey_, bytes32 bytesValue_) = iterator_.next(); - keys_[index_] = address(uint160(uint256(bytesKey_))); + keys_[index_] = uint256(bytesKey_); values_[index_] = address(uint160(uint256(bytesValue_))); if (index_++ == 2) { diff --git a/test/libs/data-structures/AvlTree.test.ts b/test/libs/data-structures/AvlTree.test.ts index 5a557830..67131cc8 100644 --- a/test/libs/data-structures/AvlTree.test.ts +++ b/test/libs/data-structures/AvlTree.test.ts @@ -13,13 +13,11 @@ describe("AvlTree", () => { let USER2: SignerWithAddress; let USER3: SignerWithAddress; let USER4: SignerWithAddress; - let USER5: SignerWithAddress; - let USER6: SignerWithAddress; let avlTree: AvlTreeMock; before(async () => { - [USER1, USER2, USER3, USER4, USER5, USER6] = await ethers.getSigners(); + [USER1, USER2, USER3, USER4] = await ethers.getSigners(); const AvlTreeMock = await ethers.getContractFactory("AvlTreeMock"); avlTree = await AvlTreeMock.deploy(); @@ -41,10 +39,10 @@ describe("AvlTree", () => { describe("Uint Tree", () => { describe("insert", () => { - it("should insert uint values to the uint tree correctly", async () => { - await avlTree.insertUintToUint(4, 1); - await avlTree.insertUintToUint(12, 2); - await avlTree.insertUintToUint(1, 3); + it("should insert values to the uint tree correctly", async () => { + await avlTree.insertUint(4, 1); + await avlTree.insertUint(12, 2); + await avlTree.insertUint(1, 3); expect(await avlTree.rootUint()).to.equal(4); expect(await avlTree.sizeUint()).to.equal(3); @@ -53,9 +51,9 @@ describe("AvlTree", () => { expect(traversal[0]).to.deep.equal([1, 4, 12]); expect(traversal[1]).to.deep.equal([3, 1, 2]); - await avlTree.insertUintToUint(200, 4); - await avlTree.insertUintToUint(10, 5); - await avlTree.insertUintToUint(5, 6); + await avlTree.insertUint(200, 4); + await avlTree.insertUint(10, 5); + await avlTree.insertUint(5, 6); expect(await avlTree.rootUint()).to.equal(10); expect(await avlTree.sizeUint()).to.equal(6); @@ -64,7 +62,7 @@ describe("AvlTree", () => { expect(traversal[0]).to.deep.equal([1, 4, 5, 10, 12, 200]); expect(traversal[1]).to.deep.equal([3, 1, 6, 5, 2, 4]); - await avlTree.insertUintToUint(15, 7); + await avlTree.insertUint(15, 7); expect(await avlTree.rootUint()).to.equal(10); expect(await avlTree.sizeUint()).to.equal(7); @@ -73,8 +71,8 @@ describe("AvlTree", () => { expect(traversal[0]).to.deep.equal([1, 4, 5, 10, 12, 15, 200]); expect(traversal[1]).to.deep.equal([3, 1, 6, 5, 2, 7, 4]); - await avlTree.insertUintToUint(2, 8); - await avlTree.insertUintToUint(3, 9); + await avlTree.insertUint(2, 8); + await avlTree.insertUint(3, 9); expect(await avlTree.rootUint()).to.equal(10); expect(await avlTree.sizeUint()).to.equal(9); @@ -84,66 +82,33 @@ describe("AvlTree", () => { expect(traversal[1]).to.deep.equal([3, 8, 9, 1, 6, 5, 2, 7, 4]); }); - it("should insert address values to the uint tree correctly", async () => { - await avlTree.insertAddressToUint(2, USER2); - await avlTree.insertAddressToUint(6, USER1); - await avlTree.insertAddressToUint(4, USER2); - - expect(await avlTree.rootUint()).to.equal(4); - expect(await avlTree.sizeUint()).to.equal(3); - - let traversal = await avlTree.traverseUint(); - expect(traversal[0]).to.deep.equal([2, 4, 6]); - expect(traversal[1]).to.deep.equal([USER2.address, USER2.address, USER1.address]); - - await avlTree.insertAddressToUint(1, USER2); - await avlTree.insertAddressToUint(13, USER4); - await avlTree.insertAddressToUint(7, USER1); - await avlTree.insertAddressToUint(5, USER3); - - expect(await avlTree.rootUint()).to.equal(4); - expect(await avlTree.sizeUint()).to.equal(7); - - traversal = await avlTree.traverseUint(); - expect(traversal[0]).to.deep.equal([1, 2, 4, 5, 6, 7, 13]); - expect(traversal[1]).to.deep.equal([ - USER2.address, - USER2.address, - USER2.address, - USER3.address, - USER1.address, - USER1.address, - USER4.address, - ]); - }); - it("should handle complex operations on the uint tree", async () => { - await avlTree.insertUintToUint(2, 10); - await avlTree.insertUintToUint(3, 1); - await avlTree.insertUintToUint(10, 18); + await avlTree.insertUint(2, 10); + await avlTree.insertUint(3, 1); + await avlTree.insertUint(10, 18); await avlTree.removeUint(3); - await avlTree.insertUintToUint(4, 20); - await avlTree.insertUintToUint(5, 10); + await avlTree.insertUint(4, 20); + await avlTree.insertUint(5, 10); await avlTree.removeUint(2); await avlTree.removeUint(10); await avlTree.removeUint(4); - await avlTree.insertUintToUint(4, 7); - await avlTree.insertUintToUint(1, 20); - await avlTree.insertUintToUint(2, 10); - await avlTree.insertUintToUint(20, 20); - await avlTree.insertUintToUint(111, 10); + await avlTree.insertUint(4, 7); + await avlTree.insertUint(1, 20); + await avlTree.insertUint(2, 10); + await avlTree.insertUint(20, 20); + await avlTree.insertUint(111, 10); await avlTree.removeUint(20); - await avlTree.insertUintToUint(16, 20); - await avlTree.insertUintToUint(17, 100); - await avlTree.insertUintToUint(18, 20); - await avlTree.insertUintToUint(19, 250); - await avlTree.insertUintToUint(20, 4); + await avlTree.insertUint(16, 20); + await avlTree.insertUint(17, 100); + await avlTree.insertUint(18, 20); + await avlTree.insertUint(19, 250); + await avlTree.insertUint(20, 4); expect(await avlTree.rootUint()).to.equal(4); expect(await avlTree.sizeUint()).to.equal(10); @@ -154,24 +119,24 @@ describe("AvlTree", () => { }); it("should not allow to insert 0 as key to the uint tree", async () => { - await expect(avlTree.insertUintToUint(0, 100)).to.be.revertedWith("AvlTree: key is not allowed to be 0"); + await expect(avlTree.insertUint(0, 100)).to.be.revertedWith("AvlTree: key is not allowed to be 0"); }); it("should not allow to insert node with duplicate key to the uint tree", async () => { - await avlTree.insertUintToUint(2, 10); - await expect(avlTree.insertUintToUint(2, 4)).to.be.revertedWith("AvlTree: the node already exists"); + await avlTree.insertUint(2, 10); + await expect(avlTree.insertUint(2, 4)).to.be.revertedWith("AvlTree: the node already exists"); }); }); describe("remove", () => { it("should remove a node in the uint tree correctly", async () => { - await avlTree.insertUintToUint(2, 4); - await avlTree.insertUintToUint(3, 8); - await avlTree.insertUintToUint(6, 10); - await avlTree.insertUintToUint(1, 2); - await avlTree.insertUintToUint(4, 10); - await avlTree.insertUintToUint(7, 10); - await avlTree.insertUintToUint(10, 10); + await avlTree.insertUint(2, 4); + await avlTree.insertUint(3, 8); + await avlTree.insertUint(6, 10); + await avlTree.insertUint(1, 2); + await avlTree.insertUint(4, 10); + await avlTree.insertUint(7, 10); + await avlTree.insertUint(10, 10); expect(await avlTree.rootUint()).to.equal(3); expect(await avlTree.sizeUint()).to.equal(7); @@ -186,15 +151,36 @@ describe("AvlTree", () => { expect(traversal[1]).to.deep.equal([2, 4, 10, 10, 10, 10]); }); + it("should handle removing root node in the uint tree correctly", async () => { + await avlTree.setUintDescComparator(); + + await avlTree.insertUint(4, 2); + await avlTree.insertUint(6, 3); + await avlTree.insertUint(2, 22); + await avlTree.insertUint(1, 12); + await avlTree.insertUint(8, 4); + + expect(await avlTree.rootUint()).to.be.equal(4); + + await avlTree.removeUint(4); + + expect(await avlTree.rootUint()).to.be.equal(2); + expect(await avlTree.sizeUint()).to.be.equal(4); + + const traversal = await avlTree.traverseUint(); + expect(traversal[0]).to.deep.equal([8, 6, 2, 1]); + expect(traversal[1]).to.deep.equal([4, 3, 22, 12]); + }); + it("should remove multiple nodes in the uint tree correctly", async () => { - await avlTree.insertUintToUint(1, 2); - await avlTree.insertUintToUint(4, 20); - await avlTree.insertUintToUint(3, 4); - await avlTree.insertUintToUint(7, 1); - await avlTree.insertUintToUint(8, 10); - await avlTree.insertUintToUint(9, 1); - await avlTree.insertUintToUint(5, 12); - await avlTree.insertUintToUint(6, 11); + await avlTree.insertUint(1, 2); + await avlTree.insertUint(4, 20); + await avlTree.insertUint(3, 4); + await avlTree.insertUint(7, 1); + await avlTree.insertUint(8, 10); + await avlTree.insertUint(9, 1); + await avlTree.insertUint(5, 12); + await avlTree.insertUint(6, 11); await avlTree.removeUint(8); await avlTree.removeUint(3); @@ -209,13 +195,13 @@ describe("AvlTree", () => { }); it("should handle removing all the nodes in the uint tree correctly", async () => { - await avlTree.insertUintToUint(2, 1); - await avlTree.insertUintToUint(4, 10); - await avlTree.insertUintToUint(3, 2); + await avlTree.insertUint(2, 1); + await avlTree.insertUint(4, 10); + await avlTree.insertUint(3, 2); await avlTree.removeUint(4); await avlTree.removeUint(2); - await avlTree.insertUintToUint(2, 2); - await avlTree.insertUintToUint(1, 2); + await avlTree.insertUint(2, 2); + await avlTree.insertUint(1, 2); await avlTree.removeUint(2); await avlTree.removeUint(1); await avlTree.removeUint(3); @@ -229,15 +215,15 @@ describe("AvlTree", () => { }); it("should not allow to remove a node that doesn't exist in the uint tree", async () => { - await avlTree.insertUintToUint(1, 10); + await avlTree.insertUint(1, 10); await expect(avlTree.removeUint(2)).to.be.revertedWith("AvlTree: the node doesn't exist"); await expect(avlTree.removeUint(0)).to.be.revertedWith("AvlTree: key is not allowed to be 0"); }); it("should not allow to remove a node twice in the uint tree", async () => { - await avlTree.insertUintToUint(1, 10); - await avlTree.insertUintToUint(2, 20); + await avlTree.insertUint(1, 10); + await avlTree.insertUint(2, 20); await avlTree.removeUint(2); @@ -253,12 +239,12 @@ describe("AvlTree", () => { it("should set a comparator for the uint tree correctly", async () => { await avlTree.setUintDescComparator(); - await avlTree.insertUintToUint(2, 3); - await avlTree.insertUintToUint(4, 10); - await avlTree.insertUintToUint(1, 4); - await avlTree.insertUintToUint(6, 4); - await avlTree.insertUintToUint(3, 200); - await avlTree.insertUintToUint(5, 1); + await avlTree.insertUint(2, 3); + await avlTree.insertUint(4, 10); + await avlTree.insertUint(1, 4); + await avlTree.insertUint(6, 4); + await avlTree.insertUint(3, 200); + await avlTree.insertUint(5, 1); expect(await avlTree.rootUint()).to.equal(4); expect(await avlTree.sizeUint()).to.equal(6); @@ -269,7 +255,7 @@ describe("AvlTree", () => { }); it("should not allow to set comparator for the uint tree if the tree is not empty", async () => { - await avlTree.insertUintToUint(1, 10); + await avlTree.insertUint(1, 10); await expect(avlTree.setUintDescComparator()).to.be.revertedWith("AvlTree: the tree must be empty"); }); @@ -277,52 +263,47 @@ describe("AvlTree", () => { describe("getters", () => { it("should get value for the existing node in the uint tree correctly", async () => { - await avlTree.insertUintToUint(1, 4); - await avlTree.insertUintToUint(2, 2); - - await avlTree.insertAddressToUint(3, USER1); - await avlTree.insertAddressToUint(4, USER2); + await avlTree.insertUint(1, 4); + await avlTree.insertUint(2, 2); + await avlTree.insertUint(3, 1); - expect(await avlTree.getUintFromUint(1)).to.be.equal(4); - expect(await avlTree.tryGetUintFromUint(1)).to.deep.equal([true, 4]); + expect(await avlTree.getUint(1)).to.be.equal(4); + expect(await avlTree.tryGetUint(1)).to.deep.equal([true, 4]); - expect(await avlTree.getUintFromUint(2)).to.deep.equal(2); - expect(await avlTree.tryGetUintFromUint(2)).to.deep.equal([true, 2]); + expect(await avlTree.getUint(2)).to.deep.equal(2); + expect(await avlTree.tryGetUint(2)).to.deep.equal([true, 2]); - await expect(avlTree.getUintFromUint(5)).to.be.revertedWith("AvlTree: the node doesn't exist"); - expect(await avlTree.tryGetUintFromUint(5)).to.deep.equal([false, 0]); + expect(await avlTree.getUint(3)).to.be.equal(1); + expect(await avlTree.tryGetUint(3)).to.deep.equal([true, 1]); - expect(await avlTree.getAddressFromUint(3)).to.be.equal(USER1.address); - expect(await avlTree.tryGetAddressFromUint(3)).to.deep.equal([true, USER1.address]); + await expect(avlTree.getUint(4)).to.be.revertedWith("AvlTree: the node doesn't exist"); + expect(await avlTree.tryGetUint(4)).to.deep.equal([false, 0]); - expect(await avlTree.getAddressFromUint(4)).to.be.equal(USER2.address); - expect(await avlTree.tryGetAddressFromUint(4)).to.deep.equal([true, USER2.address]); - - await expect(avlTree.getAddressFromUint(6)).to.be.revertedWith("AvlTree: the node doesn't exist"); - expect(await avlTree.tryGetAddressFromUint(6)).to.deep.equal([false, ZERO_ADDR]); + await expect(avlTree.getUint(6)).to.be.revertedWith("AvlTree: the node doesn't exist"); + expect(await avlTree.tryGetUint(6)).to.deep.equal([false, 0]); }); it("should handle getting value for the non-existing node in the uint tree correctly", async () => { - await expect(avlTree.getUintFromUint(1)).to.be.revertedWith("AvlTree: the node doesn't exist"); - expect(await avlTree.tryGetUintFromUint(1)).to.deep.equal([false, 0]); + await expect(avlTree.getUint(1)).to.be.revertedWith("AvlTree: the node doesn't exist"); + expect(await avlTree.tryGetUint(1)).to.deep.equal([false, 0]); - await avlTree.insertUintToUint(1, 30); + await avlTree.insertUint(1, 30); - await expect(avlTree.getUintFromUint(2)).to.be.revertedWith("AvlTree: the node doesn't exist"); - expect(await avlTree.tryGetUintFromUint(2)).to.deep.equal([false, 0]); + await expect(avlTree.getUint(2)).to.be.revertedWith("AvlTree: the node doesn't exist"); + expect(await avlTree.tryGetUint(2)).to.deep.equal([false, 0]); - await expect(avlTree.getUintFromUint(0)).to.be.revertedWith("AvlTree: the node doesn't exist"); - expect(await avlTree.tryGetUintFromUint(0)).to.deep.equal([false, 0]); + await expect(avlTree.getUint(0)).to.be.revertedWith("AvlTree: the node doesn't exist"); + expect(await avlTree.tryGetUint(0)).to.deep.equal([false, 0]); }); it("should get the treeSize correctly for the uint tree", async () => { expect(await avlTree.sizeUint()).to.be.equal(0); - await avlTree.insertUintToUint(1, 10); - await avlTree.insertUintToUint(2, 10); + await avlTree.insertUint(1, 10); + await avlTree.insertUint(2, 10); await avlTree.removeUint(1); - await avlTree.insertUintToUint(3, 4); - await avlTree.insertUintToUint(7, 90); + await avlTree.insertUint(3, 4); + await avlTree.insertUint(7, 90); expect(await avlTree.sizeUint()).to.be.equal(3); }); @@ -336,11 +317,11 @@ describe("AvlTree", () => { }); it("should traverse the uint tree correctly", async () => { - await avlTree.insertUintToUint(2, 10); - await avlTree.insertUintToUint(4, 11); - await avlTree.insertUintToUint(1, 12); - await avlTree.insertUintToUint(6, 13); - await avlTree.insertUintToUint(7, 14); + await avlTree.insertUint(2, 10); + await avlTree.insertUint(4, 11); + await avlTree.insertUint(1, 12); + await avlTree.insertUint(6, 13); + await avlTree.insertUint(7, 14); let fullTraversal = await avlTree.traverseUint(); expect(fullTraversal[0]).to.deep.equal([1, 2, 4, 6, 7]); @@ -350,7 +331,11 @@ describe("AvlTree", () => { expect(firstThreeTraversal[0]).to.deep.equal([1, 2, 4]); expect(firstThreeTraversal[1]).to.deep.equal([12, 10, 11]); - await expect(avlTree.brokenTraversalUint()).to.be.revertedWith("Traversal: No more nodes"); + let backwardsTraversal = await avlTree.backwardsTraversalUint(); + expect(backwardsTraversal[0]).to.deep.equal([7, 6, 4, 2, 1]); + expect(backwardsTraversal[1]).to.deep.equal([14, 13, 11, 10, 12]); + + await expect(avlTree.brokenTraversalUint()).to.be.revertedWith("Traversal: no more nodes"); }); }); }); @@ -359,104 +344,92 @@ describe("AvlTree", () => { it("should insert nodes to the bytes32 tree correctly", async () => { await avlTree.setBytes32DescComparator(); - await avlTree.insertUintToBytes32(encodeBytes32String("2"), 20); - await avlTree.insertUintToBytes32(encodeBytes32String("4"), 22); - await avlTree.insertUintToBytes32(encodeBytes32String("1"), 12); - await avlTree.insertUintToBytes32(encodeBytes32String("6"), 2); - await avlTree.insertUintToBytes32(encodeBytes32String("3"), 112); - await avlTree.insertUintToBytes32(encodeBytes32String("5"), 2); + await avlTree.insertBytes32(2, encodeBytes32String("20")); + await avlTree.insertBytes32(4, encodeBytes32String("22")); + await avlTree.insertBytes32(1, encodeBytes32String("12")); + await avlTree.insertBytes32(6, encodeBytes32String("2")); + await avlTree.insertBytes32(3, encodeBytes32String("112")); + await avlTree.insertBytes32(5, encodeBytes32String("2")); - expect(await avlTree.rootBytes32()).to.equal(encodeBytes32String("4")); - expect(await avlTree.getUintFromBytes32(encodeBytes32String("4"))).to.be.equal(22); - expect(await avlTree.tryGetUintFromBytes32(encodeBytes32String("4"))).to.deep.equal([true, 22]); + expect(await avlTree.rootBytes32()).to.equal(4); + expect(await avlTree.getBytes32(4)).to.be.equal(encodeBytes32String("22")); + expect(await avlTree.tryGetBytes32(4)).to.deep.equal([true, encodeBytes32String("22")]); expect(await avlTree.sizeBytes32()).to.equal(6); let fullTraversal = await avlTree.traverseBytes32(); - expect(fullTraversal[0]).to.deep.equal(uintToBytes32Array([6, 5, 4, 3, 2, 1])); - expect(fullTraversal[1]).to.deep.equal([2, 2, 22, 112, 20, 12]); + expect(fullTraversal[0]).to.deep.equal([6, 5, 4, 3, 2, 1]); + expect(fullTraversal[1]).to.deep.equal(uintToBytes32Array([2, 2, 22, 112, 20, 12])); let firstThreeTraversal = await avlTree.traverseFirstThreeBytes32(); - expect(firstThreeTraversal[0]).to.deep.equal(uintToBytes32Array([6, 5, 4])); - expect(firstThreeTraversal[1]).to.deep.equal([2, 2, 22]); + expect(firstThreeTraversal[0]).to.deep.equal([6, 5, 4]); + expect(firstThreeTraversal[1]).to.deep.equal(uintToBytes32Array([2, 2, 22])); }); it("should remove nodes in the bytes32 tree correctly", async () => { - await avlTree.insertUintToBytes32(encodeBytes32String("2"), 2); - await avlTree.insertUintToBytes32(encodeBytes32String("1"), 1); - await avlTree.insertUintToBytes32(encodeBytes32String("5"), 5); - await avlTree.insertUintToBytes32(encodeBytes32String("4"), 6); + await avlTree.insertBytes32(2, encodeBytes32String("2")); + await avlTree.insertBytes32(1, encodeBytes32String("1")); + await avlTree.insertBytes32(5, encodeBytes32String("5")); + await avlTree.insertBytes32(4, encodeBytes32String("6")); - await avlTree.removeBytes32(encodeBytes32String("2")); + await avlTree.removeBytes32(2); - expect(await avlTree.rootBytes32()).to.equal(encodeBytes32String("4")); + expect(await avlTree.rootBytes32()).to.equal(4); expect(await avlTree.sizeBytes32()).to.equal(3); - await expect(avlTree.getUintFromBytes32(encodeBytes32String("2"))).to.be.revertedWith( - "AvlTree: the node doesn't exist", - ); - expect(await avlTree.tryGetUintFromBytes32(encodeBytes32String("2"))).to.deep.equal([false, 0]); + await expect(avlTree.getBytes32(2)).to.be.revertedWith("AvlTree: the node doesn't exist"); + expect(await avlTree.tryGetBytes32(2)).to.deep.equal([false, 0]); let traversal = await avlTree.traverseBytes32(); - expect(traversal[0]).to.deep.equal(uintToBytes32Array([1, 4, 5])); - expect(traversal[1]).to.deep.equal([1, 6, 5]); + expect(traversal[0]).to.deep.equal([1, 4, 5]); + expect(traversal[1]).to.deep.equal(uintToBytes32Array([1, 6, 5])); - await avlTree.insertUintToBytes32(encodeBytes32String("3"), 3); - await avlTree.insertUintToBytes32(encodeBytes32String("6"), 4); - await avlTree.insertUintToBytes32(encodeBytes32String("2"), 2); + await avlTree.insertBytes32(3, encodeBytes32String("3")); + await avlTree.insertBytes32(6, encodeBytes32String("4")); + await avlTree.insertBytes32(2, encodeBytes32String("2")); - await avlTree.removeBytes32(encodeBytes32String("1")); - await avlTree.removeBytes32(encodeBytes32String("3")); + await avlTree.removeBytes32(1); + await avlTree.removeBytes32(3); - expect(await avlTree.rootBytes32()).to.equal(encodeBytes32String("4")); + expect(await avlTree.rootBytes32()).to.equal(4); expect(await avlTree.sizeBytes32()).to.equal(4); - expect(await avlTree.getUintFromBytes32(encodeBytes32String("2"))).to.be.equal(2); - expect(await avlTree.tryGetUintFromBytes32(encodeBytes32String("2"))).to.deep.equal([true, 2]); + expect(await avlTree.getBytes32(2)).to.be.equal(encodeBytes32String("2")); + expect(await avlTree.tryGetBytes32(2)).to.deep.equal([true, encodeBytes32String("2")]); - await expect(avlTree.getUintFromBytes32(encodeBytes32String("1"))).to.be.revertedWith( - "AvlTree: the node doesn't exist", - ); - expect(await avlTree.tryGetUintFromBytes32(encodeBytes32String("1"))).to.deep.equal([false, 0]); + await expect(avlTree.getBytes32(1)).to.be.revertedWith("AvlTree: the node doesn't exist"); + expect(await avlTree.tryGetBytes32(1)).to.deep.equal([false, encodeBytes32String("")]); - await expect(avlTree.getUintFromBytes32(encodeBytes32String("3"))).to.be.revertedWith( - "AvlTree: the node doesn't exist", - ); - expect(await avlTree.tryGetUintFromBytes32(encodeBytes32String("3"))).to.deep.equal([false, 0]); + await expect(avlTree.getBytes32(3)).to.be.revertedWith("AvlTree: the node doesn't exist"); + expect(await avlTree.tryGetBytes32(3)).to.deep.equal([false, encodeBytes32String("")]); traversal = await avlTree.traverseBytes32(); - expect(traversal[0]).to.deep.equal(uintToBytes32Array([2, 4, 5, 6])); - expect(traversal[1]).to.deep.equal([2, 6, 5, 4]); + expect(traversal[0]).to.deep.equal([2, 4, 5, 6]); + expect(traversal[1]).to.deep.equal(uintToBytes32Array([2, 6, 5, 4])); - await avlTree.removeBytes32(encodeBytes32String("2")); - await avlTree.removeBytes32(encodeBytes32String("5")); - await avlTree.removeBytes32(encodeBytes32String("6")); - await avlTree.removeBytes32(encodeBytes32String("4")); + await avlTree.removeBytes32(2); + await avlTree.removeBytes32(5); + await avlTree.removeBytes32(6); + await avlTree.removeBytes32(4); - expect(await avlTree.rootBytes32()).to.equal(encodeBytes32String("")); - expect(await avlTree.sizeBytes32()).to.equal(0); + expect(await avlTree.rootBytes32()).to.equal(0); + expect(await avlTree.sizeBytes32()).to.equal(encodeBytes32String("")); }); it("should handle getting value in the bytes32 tree correctly", async () => { - await expect(avlTree.getUintFromBytes32(encodeBytes32String("1"))).to.be.revertedWith( - "AvlTree: the node doesn't exist", - ); - expect(await avlTree.tryGetUintFromBytes32(encodeBytes32String("1"))).to.deep.equal([false, 0]); + await expect(avlTree.getBytes32(1)).to.be.revertedWith("AvlTree: the node doesn't exist"); + expect(await avlTree.tryGetBytes32(1)).to.deep.equal([false, encodeBytes32String("")]); - await avlTree.insertUintToBytes32(encodeBytes32String("1"), 18); + await avlTree.insertBytes32(1, encodeBytes32String("18")); - expect(await avlTree.getUintFromBytes32(encodeBytes32String("1"))).to.be.equal(18); - expect(await avlTree.tryGetUintFromBytes32(encodeBytes32String("1"))).to.deep.equal([true, 18]); + expect(await avlTree.getBytes32(1)).to.be.equal(encodeBytes32String("18")); + expect(await avlTree.tryGetBytes32(1)).to.deep.equal([true, encodeBytes32String("18")]); - await expect(avlTree.getUintFromBytes32(encodeBytes32String("2"))).to.be.revertedWith( - "AvlTree: the node doesn't exist", - ); - expect(await avlTree.tryGetUintFromBytes32(encodeBytes32String("2"))).to.deep.equal([false, 0]); + await expect(avlTree.getBytes32(2)).to.be.revertedWith("AvlTree: the node doesn't exist"); + expect(await avlTree.tryGetBytes32(2)).to.deep.equal([false, encodeBytes32String("")]); - await expect(avlTree.getUintFromBytes32(encodeBytes32String("0"))).to.be.revertedWith( - "AvlTree: the node doesn't exist", - ); - expect(await avlTree.tryGetUintFromBytes32(encodeBytes32String("0"))).to.deep.equal([false, 0]); + await expect(avlTree.getBytes32(0)).to.be.revertedWith("AvlTree: the node doesn't exist"); + expect(await avlTree.tryGetBytes32(0)).to.deep.equal([false, encodeBytes32String("")]); }); it("should check if custom comparator is set for the bytes32 tree correctly", async () => { @@ -470,105 +443,98 @@ describe("AvlTree", () => { describe("Address Tree", () => { it("should insert nodes to the address tree correctly", async () => { - let users = [USER1.address, USER2.address, USER3.address, USER4.address, USER5.address, USER6.address]; - let addresses = users.map((user) => user); - - addresses.sort((a, b) => a.localeCompare(b)); - - await avlTree.insertUintToAddress(addresses[5], 10); - await avlTree.insertUintToAddress(addresses[3], 22); - await avlTree.insertUintToAddress(addresses[4], 15); - await avlTree.insertUintToAddress(addresses[2], 6); - await avlTree.insertUintToAddress(addresses[1], 16); + await avlTree.insertAddress(6, USER1); + await avlTree.insertAddress(4, USER2); + await avlTree.insertAddress(5, USER1); + await avlTree.insertAddress(3, USER4); + await avlTree.insertAddress(2, USER3); - expect(await avlTree.rootAddress()).to.equal(addresses[4]); - expect(await avlTree.getUintFromAddress(addresses[4])).to.be.equal(15); - expect(await avlTree.tryGetUintFromAddress(addresses[4])).to.deep.equal([true, 15]); + expect(await avlTree.rootAddress()).to.equal(5); + expect(await avlTree.getAddressValue(5)).to.be.equal(USER1); + expect(await avlTree.tryGetAddress(5)).to.deep.equal([true, USER1.address]); expect(await avlTree.sizeAddress()).to.equal(5); let traversal = await avlTree.traverseAddress(); - expect(traversal[0]).to.deep.equal([addresses[1], addresses[2], addresses[3], addresses[4], addresses[5]]); - expect(traversal[1]).to.deep.equal([16, 6, 22, 15, 10]); + expect(traversal[0]).to.deep.equal([2, 3, 4, 5, 6]); + expect(traversal[1]).to.deep.equal([USER3.address, USER4.address, USER2.address, USER1.address, USER1.address]); - await avlTree.insertUintToAddress(addresses[0], 17); + await avlTree.insertAddress(1, USER3); - expect(await avlTree.rootAddress()).to.equal(addresses[2]); - expect(await avlTree.getUintFromAddress(addresses[2])).to.be.equal(6); - expect(await avlTree.tryGetUintFromAddress(addresses[2])).to.deep.equal([true, 6]); + expect(await avlTree.rootAddress()).to.equal(3); + expect(await avlTree.getAddressValue(3)).to.be.equal(USER4); + expect(await avlTree.tryGetAddress(3)).to.deep.equal([true, USER4.address]); expect(await avlTree.sizeAddress()).to.equal(6); traversal = await avlTree.traverseAddress(); - expect(traversal[0]).to.deep.equal([ - addresses[0], - addresses[1], - addresses[2], - addresses[3], - addresses[4], - addresses[5], + expect(traversal[0]).to.deep.equal([1, 2, 3, 4, 5, 6]); + expect(traversal[1]).to.deep.equal([ + USER3.address, + USER3.address, + USER4.address, + USER2.address, + USER1.address, + USER1.address, ]); - expect(traversal[1]).to.deep.equal([17, 16, 6, 22, 15, 10]); const firstThreeTraversal = await avlTree.traverseFirstThreeAddress(); - expect(firstThreeTraversal[0]).to.deep.equal([addresses[0], addresses[1], addresses[2]]); - expect(firstThreeTraversal[1]).to.deep.equal([17, 16, 6]); + expect(firstThreeTraversal[0]).to.deep.equal([1, 2, 3]); + expect(firstThreeTraversal[1]).to.deep.equal([USER3.address, USER3.address, USER4.address]); }); it("should remove nodes in the address tree correctly", async () => { - let users = [USER1.address, USER2.address, USER3.address, USER4.address, USER5.address, USER6.address]; - let addresses = users.map((user) => user); - - addresses.sort((a, b) => b.localeCompare(a)); - await avlTree.setAddressDescComparator(); - await avlTree.insertUintToAddress(addresses[1], 2); - await avlTree.insertUintToAddress(addresses[0], 1); - await avlTree.insertUintToAddress(addresses[4], 5); - await avlTree.insertUintToAddress(addresses[5], 6); + await avlTree.insertAddress(2, USER2); + await avlTree.insertAddress(1, USER2); + await avlTree.insertAddress(5, USER3); + await avlTree.insertAddress(6, USER1); - await avlTree.removeAddress(addresses[1]); + await avlTree.removeAddress(2); - expect(await avlTree.rootAddress()).to.equal(addresses[4]); + expect(await avlTree.rootAddress()).to.equal(5); expect(await avlTree.sizeAddress()).to.equal(3); - await expect(avlTree.getUintFromAddress(addresses[1])).to.be.revertedWith("AvlTree: the node doesn't exist"); - expect(await avlTree.tryGetUintFromAddress(addresses[1])).to.deep.equal([false, 0]); + await expect(avlTree.getAddressValue(2)).to.be.revertedWith("AvlTree: the node doesn't exist"); + expect(await avlTree.tryGetAddress(2)).to.deep.equal([false, ZERO_ADDR]); let traversal = await avlTree.traverseAddress(); - expect(traversal[0]).to.deep.equal([addresses[0], addresses[4], addresses[5]]); - expect(traversal[1]).to.deep.equal([1, 5, 6]); + expect(traversal[0]).to.deep.equal([6, 5, 1]); + expect(traversal[1]).to.deep.equal([USER1.address, USER3.address, USER2.address]); + + await avlTree.insertAddress(3, USER2); + await avlTree.insertAddress(4, USER4); + await avlTree.insertAddress(2, USER3); - await avlTree.insertUintToAddress(addresses[2], 3); - await avlTree.insertUintToAddress(addresses[3], 4); - await avlTree.insertUintToAddress(addresses[1], 2); + await avlTree.removeAddress(2); + await avlTree.insertAddress(2, USER2); - await avlTree.removeAddress(addresses[2]); - await avlTree.removeAddress(addresses[4]); + await avlTree.removeAddress(3); + await avlTree.removeAddress(5); - expect(await avlTree.rootAddress()).to.equal(addresses[3]); + expect(await avlTree.rootAddress()).to.equal(2); expect(await avlTree.sizeAddress()).to.equal(4); - expect(await avlTree.getUintFromAddress(addresses[0])).to.be.equal(1); - expect(await avlTree.tryGetUintFromAddress(addresses[0])).to.deep.equal([true, 1]); + expect(await avlTree.getAddressValue(1)).to.be.equal(USER2); + expect(await avlTree.tryGetAddress(1)).to.deep.equal([true, USER2.address]); - await expect(avlTree.getUintFromAddress(addresses[2])).to.be.revertedWith("AvlTree: the node doesn't exist"); - expect(await avlTree.tryGetUintFromAddress(addresses[2])).to.deep.equal([false, 0]); + await expect(avlTree.getAddressValue(3)).to.be.revertedWith("AvlTree: the node doesn't exist"); + expect(await avlTree.tryGetAddress(3)).to.deep.equal([false, 0]); - await expect(avlTree.getUintFromAddress(addresses[4])).to.be.revertedWith("AvlTree: the node doesn't exist"); - expect(await avlTree.tryGetUintFromAddress(addresses[4])).to.deep.equal([false, 0]); + await expect(avlTree.getAddressValue(5)).to.be.revertedWith("AvlTree: the node doesn't exist"); + expect(await avlTree.tryGetAddress(5)).to.deep.equal([false, ZERO_ADDR]); traversal = await avlTree.traverseAddress(); - expect(traversal[0]).to.deep.equal([addresses[0], addresses[1], addresses[3], addresses[5]]); - expect(traversal[1]).to.deep.equal([1, 2, 4, 6]); + expect(traversal[0]).to.deep.equal([6, 4, 2, 1]); + expect(traversal[1]).to.deep.equal([USER1.address, USER4.address, USER2.address, USER2.address]); - await avlTree.removeAddress(addresses[1]); - await avlTree.removeAddress(addresses[5]); - await avlTree.removeAddress(addresses[3]); - await avlTree.removeAddress(addresses[0]); + await avlTree.removeAddress(2); + await avlTree.removeAddress(6); + await avlTree.removeAddress(4); + await avlTree.removeAddress(1); - expect(await avlTree.rootAddress()).to.equal(ZERO_ADDR); + expect(await avlTree.rootAddress()).to.equal(0); expect(await avlTree.sizeAddress()).to.equal(0); traversal = await avlTree.traverseAddress(); @@ -577,19 +543,19 @@ describe("AvlTree", () => { }); it("should handle getting value in the address tree correctly", async () => { - await expect(avlTree.getUintFromAddress(USER1)).to.be.revertedWith("AvlTree: the node doesn't exist"); - expect(await avlTree.tryGetUintFromAddress(USER1)).to.deep.equal([false, 0]); + await expect(avlTree.getAddressValue(1)).to.be.revertedWith("AvlTree: the node doesn't exist"); + expect(await avlTree.tryGetAddress(1)).to.deep.equal([false, ZERO_ADDR]); - await avlTree.insertUintToAddress(USER1, 2); + await avlTree.insertAddress(1, USER2); - expect(await avlTree.getUintFromAddress(USER1)).to.be.equal(2); - expect(await avlTree.tryGetUintFromAddress(USER1)).to.deep.equal([true, 2]); + expect(await avlTree.getAddressValue(1)).to.be.equal(USER2); + expect(await avlTree.tryGetAddress(1)).to.deep.equal([true, USER2.address]); - await expect(avlTree.getUintFromAddress(USER5)).to.be.revertedWith("AvlTree: the node doesn't exist"); - expect(await avlTree.tryGetUintFromAddress(USER5)).to.deep.equal([false, 0]); + await expect(avlTree.getAddressValue(5)).to.be.revertedWith("AvlTree: the node doesn't exist"); + expect(await avlTree.tryGetAddress(5)).to.deep.equal([false, ZERO_ADDR]); - await expect(avlTree.getUintFromAddress(ZERO_ADDR)).to.be.revertedWith("AvlTree: the node doesn't exist"); - expect(await avlTree.tryGetUintFromAddress(ZERO_ADDR)).to.deep.equal([false, 0]); + await expect(avlTree.getAddressValue(0)).to.be.revertedWith("AvlTree: the node doesn't exist"); + expect(await avlTree.tryGetAddress(0)).to.deep.equal([false, ZERO_ADDR]); }); it("should check if custom comparator is set for the address tree correctly", async () => { From 9adf89452f54b99c53f82f7b94ab8330973a65b4 Mon Sep 17 00:00:00 2001 From: mllwchrry Date: Fri, 7 Jun 2024 13:53:00 +0300 Subject: [PATCH 07/11] fixed natspec, removed Typecaster --- contracts/libs/data-structures/AvlTree.sol | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/contracts/libs/data-structures/AvlTree.sol b/contracts/libs/data-structures/AvlTree.sol index 30618752..a40e5788 100644 --- a/contracts/libs/data-structures/AvlTree.sol +++ b/contracts/libs/data-structures/AvlTree.sol @@ -3,15 +3,14 @@ pragma solidity ^0.8.4; import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; -import {TypeCaster} from "../utils/TypeCaster.sol"; - /** * @notice AVL Tree module * - * This library provides implementation of three sets with dynamic key types: + * This library provides implementation of three sets with dynamic `value` types: * `UintAVL`, `Bytes32AVL` and `Bytes32AVL`. * - * Each element in the tree contains a bytes32 `value` field to allow storing different types of values + * Each element in the tree has a bytes32 `key` field to allow storing values + * associated with different types of keys * * The implementation supports setting custom comparator function * @@ -45,8 +44,6 @@ import {TypeCaster} from "../utils/TypeCaster.sol"; * ``` */ library AvlTree { - using TypeCaster for *; - /** ********************* * UintAVL * From 6a83bd6df4454cfc57d6d7592441af5fda50a327 Mon Sep 17 00:00:00 2001 From: mllwchrry Date: Sat, 8 Jun 2024 00:13:30 +0300 Subject: [PATCH 08/11] fixed removal, added more tests --- contracts/libs/data-structures/AvlTree.sol | 41 +++- .../mock/libs/data-structures/AvlTreeMock.sol | 180 ++++++++++-------- test/libs/data-structures/AvlTree.test.ts | 67 +++++-- 3 files changed, 191 insertions(+), 97 deletions(-) diff --git a/contracts/libs/data-structures/AvlTree.sol b/contracts/libs/data-structures/AvlTree.sol index a40e5788..1c0c04d8 100644 --- a/contracts/libs/data-structures/AvlTree.sol +++ b/contracts/libs/data-structures/AvlTree.sol @@ -7,7 +7,7 @@ import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; * @notice AVL Tree module * * This library provides implementation of three sets with dynamic `value` types: - * `UintAVL`, `Bytes32AVL` and `Bytes32AVL`. + * `UintAVL`, `Bytes32AVL` and `AddressAVL`. * * Each element in the tree has a bytes32 `key` field to allow storing values * associated with different types of keys @@ -27,6 +27,7 @@ import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; * * ``` * using AvlTree for AvlTree.UintAVL; + * using Traversal for Traversal.Iterator; * * AvlTree.UintAVL internal uintTree; * @@ -34,13 +35,24 @@ import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; * * uintTree.setComparator(comparatorFunction); * - * uintTree.insert(1, bytes32(1234)); + * uintTree.insert(bytes32(1), 1234); + * uintTree.insert(bytes32(3), 100); * - * uintTree.tryGet(1); + * uintTree.tryGet(bytes32(1)); * - * uintTree.remove(1); + * uintTree.remove(bytes32(1)); * - * uintTree.size(); + * ................................................ + * + * Traversal.Iterator memory iterator_ = uintTree.first(); + * + * bytes32[] memory keys_ = new bytes32[](_uintTree.size()); + * bytes32[] memory values_ = new bytes32[](_uintTree.size()); + * + * while (iterator_.isValid()) { + * (keys_[i], values_[i]) = iterator_.value(); + * iterator_.next(); + * } * ``` */ library AvlTree { @@ -169,7 +181,7 @@ library AvlTree { } /** - * @notice The function to set a custom comparator function, that will be used to build the byte32 tree. + * @notice The function to set a custom comparator function, that will be used to build the bytes32 tree. * @param tree self. * @param comparator_ The function that accepts keys and values of the nodes to compare. */ @@ -523,13 +535,19 @@ library AvlTree { for (temp_ = right_; _tree[temp_].left != 0; temp_ = _tree[temp_].left) {} - _tree[temp_].right = _removeMin(_tree, right_); + right_ = _removeMin(_tree, right_); + _tree[temp_].left = left_; + _tree[temp_].right = right_; if (left_ != 0) { _tree[left_].parent = temp_; } + if (right_ != 0) { + _tree[right_].parent = temp_; + } + _tree[temp_].parent = parent_; return _balance(_tree, temp_); @@ -747,6 +765,15 @@ library Traversal { uint64 currentNode; } + /** + * @notice The function to check if the iterator is currently valid (has not reached the end of the traversal). + * @param iterator_ self. + * @return True if the iterator is valid, false otherwise. + */ + function isValid(Iterator memory iterator_) internal pure returns (bool) { + return iterator_.currentNode != 0; + } + /** * @notice The function to check if there is a next node in the traversal. * @param iterator_ self. diff --git a/contracts/mock/libs/data-structures/AvlTreeMock.sol b/contracts/mock/libs/data-structures/AvlTreeMock.sol index 78f44b7c..10d95d60 100644 --- a/contracts/mock/libs/data-structures/AvlTreeMock.sol +++ b/contracts/mock/libs/data-structures/AvlTreeMock.sol @@ -101,21 +101,16 @@ contract AvlTreeMock { uint256[] memory keys_ = new uint256[](_uintTree.size()); uint256[] memory values_ = new uint256[](_uintTree.size()); - if (keys_.length != 0) { - (bytes32 bytesKey_, bytes32 bytesValue_) = iterator_.value(); - - keys_[0] = uint256(bytesKey_); - values_[0] = uint256(bytesValue_); - } - - uint256 index_ = 1; + uint256 index_ = 0; - while (iterator_.hasNext()) { - (bytes32 bytesKey_, bytes32 bytesValue_) = iterator_.next(); + while (iterator_.isValid()) { + (bytes32 bytesKey_, bytes32 bytesValue_) = iterator_.value(); keys_[index_] = uint256(bytesKey_); values_[index_] = uint256(bytesValue_); + iterator_.next(); + index_++; } @@ -128,24 +123,21 @@ contract AvlTreeMock { uint256[] memory keys_ = new uint256[](3); uint256[] memory values_ = new uint256[](3); - if (keys_.length != 0) { - (bytes32 bytesKey_, bytes32 bytesValue_) = iterator_.value(); - - keys_[0] = uint256(bytesKey_); - values_[0] = uint256(bytesValue_); - } + uint256 index_ = 0; - uint256 index_ = 1; - - while (iterator_.hasNext()) { - (bytes32 bytesKey_, bytes32 bytesValue_) = iterator_.next(); + while (iterator_.isValid()) { + (bytes32 bytesKey_, bytes32 bytesValue_) = iterator_.value(); keys_[index_] = uint256(bytesKey_); values_[index_] = uint256(bytesValue_); - if (index_++ == 2) { - iterator_ = _uintTree.last(); + if (index_ == 2) { + break; } + + iterator_.next(); + + index_++; } return (keys_, values_); @@ -157,20 +149,19 @@ contract AvlTreeMock { uint256[] memory keys_ = new uint256[](_uintTree.size()); uint256[] memory values_ = new uint256[](_uintTree.size()); - (bytes32 bytesKey_, bytes32 bytesValue_) = iterator_.value(); - - keys_[0] = uint256(bytesKey_); - values_[0] = uint256(bytesValue_); + uint256 index_ = 0; - uint256 index_ = 1; - - while (iterator_.hasNext()) { + while (iterator_.isValid()) { iterator_.currentNode = 0; - (bytesKey_, bytesValue_) = iterator_.next(); + (bytes32 bytesKey_, bytes32 bytesValue_) = iterator_.value(); keys_[index_] = uint256(bytesKey_); values_[index_] = uint256(bytesValue_); + + iterator_.next(); + + index_++; } } @@ -180,82 +171,121 @@ contract AvlTreeMock { uint256[] memory keys_ = new uint256[](_uintTree.size()); uint256[] memory values_ = new uint256[](_uintTree.size()); - if (keys_.length != 0) { - (bytes32 bytesKey_, bytes32 bytesValue_) = iterator_.value(); - - keys_[0] = uint256(bytesKey_); - values_[0] = uint256(bytesValue_); - } - - uint256 index_ = 1; + uint256 index_ = 0; - while (iterator_.hasPrev()) { - (bytes32 bytesKey_, bytes32 bytesValue_) = iterator_.prev(); + while (iterator_.isValid()) { + (bytes32 bytesKey_, bytes32 bytesValue_) = iterator_.value(); keys_[index_] = uint256(bytesKey_); values_[index_] = uint256(bytesValue_); + iterator_.prev(); + index_++; } return (keys_, values_); } + function backAndForthTraverseUint() + external + view + returns (uint256[] memory, uint256[] memory) + { + Traversal.Iterator memory iterator_ = _uintTree.first(); + + uint256[] memory keys_ = new uint256[](12); + uint256[] memory values_ = new uint256[](12); + + bool[12] memory directions_ = [ + true, + true, + false, + true, + false, + false, + true, + true, + true, + true, + true, + true + ]; + + bytes32 bytesKey_; + bytes32 bytesValue_; + + for (uint256 i = 0; i < 12; i++) { + (bytesKey_, bytesValue_) = iterator_.value(); + + keys_[i] = uint256(bytesKey_); + values_[i] = uint256(bytesValue_); + + directions_[i] ? iterator_.next() : iterator_.prev(); + } + + return (keys_, values_); + } + + function nextOnLast() external view returns (uint256, uint256) { + Traversal.Iterator memory iterator_ = _uintTree.last(); + + (bytes32 key_, bytes32 value_) = iterator_.next(); + + return (uint256(key_), uint256(value_)); + } + + function prevOnFirst() external view returns (uint256, uint256) { + Traversal.Iterator memory iterator_ = _uintTree.first(); + + (bytes32 key_, bytes32 value_) = iterator_.prev(); + + return (uint256(key_), uint256(value_)); + } + function traverseBytes32() external view returns (uint256[] memory, bytes32[] memory) { Traversal.Iterator memory iterator_ = _bytes32Tree.first(); uint256[] memory keys_ = new uint256[](_bytes32Tree.size()); bytes32[] memory values_ = new bytes32[](_bytes32Tree.size()); - if (keys_.length != 0) { - (bytes32 bytesKey_, bytes32 bytesValue_) = iterator_.value(); - - keys_[0] = uint256(bytesKey_); - values_[0] = bytesValue_; - } - - uint256 index_ = 1; + uint256 index_ = 0; - while (iterator_.hasNext()) { - (bytes32 bytesKey_, bytes32 bytesValue_) = iterator_.next(); + while (iterator_.isValid()) { + (bytes32 bytesKey_, bytes32 bytesValue_) = iterator_.value(); keys_[index_] = uint256(bytesKey_); values_[index_] = bytesValue_; + iterator_.next(); + index_++; } return (keys_, values_); } - function traverseFirstThreeBytes32() + function backwardsTraversalBytes32() external view returns (uint256[] memory, bytes32[] memory) { - Traversal.Iterator memory iterator_ = _bytes32Tree.first(); - - uint256[] memory keys_ = new uint256[](3); - bytes32[] memory values_ = new bytes32[](3); - - if (keys_.length != 0) { - (bytes32 bytesKey_, bytes32 bytesValue_) = iterator_.value(); + Traversal.Iterator memory iterator_ = _bytes32Tree.last(); - keys_[0] = uint256(bytesKey_); - values_[0] = bytesValue_; - } + uint256[] memory keys_ = new uint256[](_bytes32Tree.size()); + bytes32[] memory values_ = new bytes32[](_bytes32Tree.size()); - uint256 index_ = 1; + uint256 index_ = 0; - while (iterator_.hasNext()) { - (bytes32 bytesKey_, bytes32 bytesValue_) = iterator_.next(); + while (iterator_.isValid()) { + (bytes32 bytesKey_, bytes32 bytesValue_) = iterator_.value(); keys_[index_] = uint256(bytesKey_); values_[index_] = bytesValue_; - if (index_++ == 2) { - iterator_ = _bytes32Tree.last(); - } + iterator_.prev(); + + index_++; } return (keys_, values_); @@ -288,15 +318,15 @@ contract AvlTreeMock { return (keys_, values_); } - function traverseFirstThreeAddress() + function backwardsTraversalAddress() external view returns (uint256[] memory, address[] memory) { - Traversal.Iterator memory iterator_ = _addressTree.first(); + Traversal.Iterator memory iterator_ = _addressTree.last(); - uint256[] memory keys_ = new uint256[](3); - address[] memory values_ = new address[](3); + uint256[] memory keys_ = new uint256[](_addressTree.size()); + address[] memory values_ = new address[](_addressTree.size()); if (keys_.length != 0) { (bytes32 bytesKey_, bytes32 bytesValue_) = iterator_.value(); @@ -307,15 +337,13 @@ contract AvlTreeMock { uint256 index_ = 1; - while (iterator_.hasNext()) { - (bytes32 bytesKey_, bytes32 bytesValue_) = iterator_.next(); + while (iterator_.hasPrev()) { + (bytes32 bytesKey_, bytes32 bytesValue_) = iterator_.prev(); keys_[index_] = uint256(bytesKey_); values_[index_] = address(uint160(uint256(bytesValue_))); - if (index_++ == 2) { - iterator_ = _addressTree.last(); - } + index_++; } return (keys_, values_); diff --git a/test/libs/data-structures/AvlTree.test.ts b/test/libs/data-structures/AvlTree.test.ts index 67131cc8..17bfbe35 100644 --- a/test/libs/data-structures/AvlTree.test.ts +++ b/test/libs/data-structures/AvlTree.test.ts @@ -264,14 +264,14 @@ describe("AvlTree", () => { describe("getters", () => { it("should get value for the existing node in the uint tree correctly", async () => { await avlTree.insertUint(1, 4); - await avlTree.insertUint(2, 2); + await avlTree.insertUint(2, 0); await avlTree.insertUint(3, 1); expect(await avlTree.getUint(1)).to.be.equal(4); expect(await avlTree.tryGetUint(1)).to.deep.equal([true, 4]); - expect(await avlTree.getUint(2)).to.deep.equal(2); - expect(await avlTree.tryGetUint(2)).to.deep.equal([true, 2]); + expect(await avlTree.getUint(2)).to.deep.equal(0); + expect(await avlTree.tryGetUint(2)).to.deep.equal([true, 0]); expect(await avlTree.getUint(3)).to.be.equal(1); expect(await avlTree.tryGetUint(3)).to.deep.equal([true, 1]); @@ -335,8 +335,32 @@ describe("AvlTree", () => { expect(backwardsTraversal[0]).to.deep.equal([7, 6, 4, 2, 1]); expect(backwardsTraversal[1]).to.deep.equal([14, 13, 11, 10, 12]); + expect(await avlTree.nextOnLast()).to.deep.equal([0, 0]); + expect(await avlTree.prevOnFirst()).to.deep.equal([0, 0]); + await expect(avlTree.brokenTraversalUint()).to.be.revertedWith("Traversal: no more nodes"); }); + + it("should maintain idempotent traversal", async () => { + await avlTree.insertUint(1, 12); + await avlTree.insertUint(6, 22); + await avlTree.insertUint(3, 10); + + await avlTree.removeUint(1); + + await avlTree.insertUint(2, 0); + + await avlTree.removeUint(3); + + await avlTree.insertUint(5, 5); + await avlTree.insertUint(1, 15); + await avlTree.insertUint(3, 1000); + await avlTree.insertUint(4, 44); + + const traversal = await avlTree.backAndForthTraverseUint(); + expect(traversal[0]).to.deep.equal([1, 2, 3, 2, 3, 2, 1, 2, 3, 4, 5, 6]); + expect(traversal[1]).to.deep.equal([15, 0, 1000, 0, 1000, 0, 15, 0, 1000, 44, 5, 22]); + }); }); }); @@ -361,9 +385,9 @@ describe("AvlTree", () => { expect(fullTraversal[0]).to.deep.equal([6, 5, 4, 3, 2, 1]); expect(fullTraversal[1]).to.deep.equal(uintToBytes32Array([2, 2, 22, 112, 20, 12])); - let firstThreeTraversal = await avlTree.traverseFirstThreeBytes32(); - expect(firstThreeTraversal[0]).to.deep.equal([6, 5, 4]); - expect(firstThreeTraversal[1]).to.deep.equal(uintToBytes32Array([2, 2, 22])); + let backwardsTraversal = await avlTree.backwardsTraversalBytes32(); + expect(backwardsTraversal[0]).to.deep.equal([1, 2, 3, 4, 5, 6]); + expect(backwardsTraversal[1]).to.deep.equal(uintToBytes32Array([12, 20, 112, 22, 2, 2])); }); it("should remove nodes in the bytes32 tree correctly", async () => { @@ -420,13 +444,17 @@ describe("AvlTree", () => { await expect(avlTree.getBytes32(1)).to.be.revertedWith("AvlTree: the node doesn't exist"); expect(await avlTree.tryGetBytes32(1)).to.deep.equal([false, encodeBytes32String("")]); - await avlTree.insertBytes32(1, encodeBytes32String("18")); + await avlTree.insertBytes32(1, encodeBytes32String("")); + await avlTree.insertBytes32(2, encodeBytes32String("2")); - expect(await avlTree.getBytes32(1)).to.be.equal(encodeBytes32String("18")); - expect(await avlTree.tryGetBytes32(1)).to.deep.equal([true, encodeBytes32String("18")]); + expect(await avlTree.getBytes32(1)).to.be.equal(encodeBytes32String("")); + expect(await avlTree.tryGetBytes32(1)).to.deep.equal([true, encodeBytes32String("")]); - await expect(avlTree.getBytes32(2)).to.be.revertedWith("AvlTree: the node doesn't exist"); - expect(await avlTree.tryGetBytes32(2)).to.deep.equal([false, encodeBytes32String("")]); + expect(await avlTree.getBytes32(2)).to.be.equal(encodeBytes32String("2")); + expect(await avlTree.tryGetBytes32(2)).to.deep.equal([true, encodeBytes32String("2")]); + + await expect(avlTree.getBytes32(3)).to.be.revertedWith("AvlTree: the node doesn't exist"); + expect(await avlTree.tryGetBytes32(3)).to.deep.equal([false, encodeBytes32String("")]); await expect(avlTree.getBytes32(0)).to.be.revertedWith("AvlTree: the node doesn't exist"); expect(await avlTree.tryGetBytes32(0)).to.deep.equal([false, encodeBytes32String("")]); @@ -478,9 +506,16 @@ describe("AvlTree", () => { USER1.address, ]); - const firstThreeTraversal = await avlTree.traverseFirstThreeAddress(); - expect(firstThreeTraversal[0]).to.deep.equal([1, 2, 3]); - expect(firstThreeTraversal[1]).to.deep.equal([USER3.address, USER3.address, USER4.address]); + const backwardsTraversal = await avlTree.backwardsTraversalAddress(); + expect(backwardsTraversal[0]).to.deep.equal([6, 5, 4, 3, 2, 1]); + expect(backwardsTraversal[1]).to.deep.equal([ + USER1.address, + USER1.address, + USER2.address, + USER4.address, + USER3.address, + USER3.address, + ]); }); it("should remove nodes in the address tree correctly", async () => { @@ -547,10 +582,14 @@ describe("AvlTree", () => { expect(await avlTree.tryGetAddress(1)).to.deep.equal([false, ZERO_ADDR]); await avlTree.insertAddress(1, USER2); + await avlTree.insertAddress(2, ZERO_ADDR); expect(await avlTree.getAddressValue(1)).to.be.equal(USER2); expect(await avlTree.tryGetAddress(1)).to.deep.equal([true, USER2.address]); + expect(await avlTree.getAddressValue(2)).to.be.equal(ZERO_ADDR); + expect(await avlTree.tryGetAddress(2)).to.deep.equal([true, ZERO_ADDR]); + await expect(avlTree.getAddressValue(5)).to.be.revertedWith("AvlTree: the node doesn't exist"); expect(await avlTree.tryGetAddress(5)).to.deep.equal([false, ZERO_ADDR]); From c0f9adf752aacb68fe553e02e92698d96415777a Mon Sep 17 00:00:00 2001 From: Artem Chystiakov Date: Sun, 9 Jun 2024 15:04:24 +0300 Subject: [PATCH 09/11] clean up --- .../mock/libs/data-structures/AvlTreeMock.sol | 240 +++++------------- test/libs/data-structures/AvlTree.test.ts | 11 +- 2 files changed, 71 insertions(+), 180 deletions(-) diff --git a/contracts/mock/libs/data-structures/AvlTreeMock.sol b/contracts/mock/libs/data-structures/AvlTreeMock.sol index 10d95d60..5c39384d 100644 --- a/contracts/mock/libs/data-structures/AvlTreeMock.sol +++ b/contracts/mock/libs/data-structures/AvlTreeMock.sol @@ -1,9 +1,12 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.4; +import {TypeCaster} from "../../../libs/utils/TypeCaster.sol"; + import {Traversal, AvlTree} from "../../../libs/data-structures/AvlTree.sol"; contract AvlTreeMock { + using TypeCaster for *; using AvlTree for *; using Traversal for *; @@ -96,95 +99,30 @@ contract AvlTreeMock { } function traverseUint() external view returns (uint256[] memory, uint256[] memory) { - Traversal.Iterator memory iterator_ = _uintTree.first(); - - uint256[] memory keys_ = new uint256[](_uintTree.size()); - uint256[] memory values_ = new uint256[](_uintTree.size()); - - uint256 index_ = 0; - - while (iterator_.isValid()) { - (bytes32 bytesKey_, bytes32 bytesValue_) = iterator_.value(); - - keys_[index_] = uint256(bytesKey_); - values_[index_] = uint256(bytesValue_); - - iterator_.next(); - - index_++; - } + (uint256[] memory keys_, bytes32[] memory values_) = _traverseAll( + _uintTree.first(), + _uintTree.size(), + true + ); - return (keys_, values_); + return (keys_, values_.asUint256Array()); } - function traverseFirstThreeUint() external view returns (uint256[] memory, uint256[] memory) { - Traversal.Iterator memory iterator_ = _uintTree.first(); - - uint256[] memory keys_ = new uint256[](3); - uint256[] memory values_ = new uint256[](3); - - uint256 index_ = 0; - - while (iterator_.isValid()) { - (bytes32 bytesKey_, bytes32 bytesValue_) = iterator_.value(); - - keys_[index_] = uint256(bytesKey_); - values_[index_] = uint256(bytesValue_); - - if (index_ == 2) { - break; - } - - iterator_.next(); - - index_++; - } + function backwardsTraversalUint() external view returns (uint256[] memory, uint256[] memory) { + (uint256[] memory keys_, bytes32[] memory values_) = _traverseAll( + _uintTree.last(), + _uintTree.size(), + false + ); - return (keys_, values_); + return (keys_, values_.asUint256Array()); } function brokenTraversalUint() external view { Traversal.Iterator memory iterator_ = _uintTree.first(); - uint256[] memory keys_ = new uint256[](_uintTree.size()); - uint256[] memory values_ = new uint256[](_uintTree.size()); - - uint256 index_ = 0; - - while (iterator_.isValid()) { - iterator_.currentNode = 0; - - (bytes32 bytesKey_, bytes32 bytesValue_) = iterator_.value(); - - keys_[index_] = uint256(bytesKey_); - values_[index_] = uint256(bytesValue_); - - iterator_.next(); - - index_++; - } - } - - function backwardsTraversalUint() external view returns (uint256[] memory, uint256[] memory) { - Traversal.Iterator memory iterator_ = _uintTree.last(); - - uint256[] memory keys_ = new uint256[](_uintTree.size()); - uint256[] memory values_ = new uint256[](_uintTree.size()); - - uint256 index_ = 0; - - while (iterator_.isValid()) { - (bytes32 bytesKey_, bytes32 bytesValue_) = iterator_.value(); - - keys_[index_] = uint256(bytesKey_); - values_[index_] = uint256(bytesValue_); - - iterator_.prev(); - - index_++; - } - - return (keys_, values_); + iterator_.currentNode = 0; + iterator_.next(); } function backAndForthTraverseUint() @@ -227,42 +165,8 @@ contract AvlTreeMock { return (keys_, values_); } - function nextOnLast() external view returns (uint256, uint256) { - Traversal.Iterator memory iterator_ = _uintTree.last(); - - (bytes32 key_, bytes32 value_) = iterator_.next(); - - return (uint256(key_), uint256(value_)); - } - - function prevOnFirst() external view returns (uint256, uint256) { - Traversal.Iterator memory iterator_ = _uintTree.first(); - - (bytes32 key_, bytes32 value_) = iterator_.prev(); - - return (uint256(key_), uint256(value_)); - } - function traverseBytes32() external view returns (uint256[] memory, bytes32[] memory) { - Traversal.Iterator memory iterator_ = _bytes32Tree.first(); - - uint256[] memory keys_ = new uint256[](_bytes32Tree.size()); - bytes32[] memory values_ = new bytes32[](_bytes32Tree.size()); - - uint256 index_ = 0; - - while (iterator_.isValid()) { - (bytes32 bytesKey_, bytes32 bytesValue_) = iterator_.value(); - - keys_[index_] = uint256(bytesKey_); - values_[index_] = bytesValue_; - - iterator_.next(); - - index_++; - } - - return (keys_, values_); + return _traverseAll(_bytes32Tree.first(), _bytes32Tree.size(), true); } function backwardsTraversalBytes32() @@ -270,52 +174,17 @@ contract AvlTreeMock { view returns (uint256[] memory, bytes32[] memory) { - Traversal.Iterator memory iterator_ = _bytes32Tree.last(); - - uint256[] memory keys_ = new uint256[](_bytes32Tree.size()); - bytes32[] memory values_ = new bytes32[](_bytes32Tree.size()); - - uint256 index_ = 0; - - while (iterator_.isValid()) { - (bytes32 bytesKey_, bytes32 bytesValue_) = iterator_.value(); - - keys_[index_] = uint256(bytesKey_); - values_[index_] = bytesValue_; - - iterator_.prev(); - - index_++; - } - - return (keys_, values_); + return _traverseAll(_bytes32Tree.last(), _bytes32Tree.size(), false); } function traverseAddress() external view returns (uint256[] memory, address[] memory) { - Traversal.Iterator memory iterator_ = _addressTree.first(); - - uint256[] memory keys_ = new uint256[](_addressTree.size()); - address[] memory values_ = new address[](_addressTree.size()); - - if (keys_.length != 0) { - (bytes32 bytesKey_, bytes32 bytesValue_) = iterator_.value(); - - keys_[0] = uint256(bytesKey_); - values_[0] = address(uint160(uint256(bytesValue_))); - } - - uint256 index_ = 1; - - while (iterator_.hasNext()) { - (bytes32 bytesKey_, bytes32 bytesValue_) = iterator_.next(); - - keys_[index_] = uint256(bytesKey_); - values_[index_] = address(uint160(uint256(bytesValue_))); - - index_++; - } + (uint256[] memory keys_, bytes32[] memory values_) = _traverseAll( + _addressTree.first(), + _addressTree.size(), + true + ); - return (keys_, values_); + return (keys_, values_.asAddressArray()); } function backwardsTraversalAddress() @@ -323,30 +192,29 @@ contract AvlTreeMock { view returns (uint256[] memory, address[] memory) { - Traversal.Iterator memory iterator_ = _addressTree.last(); + (uint256[] memory keys_, bytes32[] memory values_) = _traverseAll( + _addressTree.last(), + _addressTree.size(), + false + ); - uint256[] memory keys_ = new uint256[](_addressTree.size()); - address[] memory values_ = new address[](_addressTree.size()); - - if (keys_.length != 0) { - (bytes32 bytesKey_, bytes32 bytesValue_) = iterator_.value(); + return (keys_, values_.asAddressArray()); + } - keys_[0] = uint256(bytesKey_); - values_[0] = address(uint160(uint256(bytesValue_))); - } + function nextOnLast() external view returns (uint256, uint256) { + Traversal.Iterator memory iterator_ = _uintTree.last(); - uint256 index_ = 1; + (bytes32 key_, bytes32 value_) = iterator_.next(); - while (iterator_.hasPrev()) { - (bytes32 bytesKey_, bytes32 bytesValue_) = iterator_.prev(); + return (uint256(key_), uint256(value_)); + } - keys_[index_] = uint256(bytesKey_); - values_[index_] = address(uint160(uint256(bytesValue_))); + function prevOnFirst() external view returns (uint256, uint256) { + Traversal.Iterator memory iterator_ = _uintTree.first(); - index_++; - } + (bytes32 key_, bytes32 value_) = iterator_.prev(); - return (keys_, values_); + return (uint256(key_), uint256(value_)); } function isCustomComparatorSetUint() external view returns (bool) { @@ -361,6 +229,30 @@ contract AvlTreeMock { return _addressTree.isCustomComparatorSet(); } + function _traverseAll( + Traversal.Iterator memory iterator_, + uint256 size_, + bool direction_ + ) private view returns (uint256[] memory, bytes32[] memory) { + uint256[] memory keys_ = new uint256[](size_); + bytes32[] memory values_ = new bytes32[](size_); + + uint256 index_; + + while (iterator_.isValid()) { + (bytes32 bytesKey_, bytes32 bytesValue_) = iterator_.value(); + + keys_[index_] = uint256(bytesKey_); + values_[index_] = bytesValue_; + + direction_ ? iterator_.next() : iterator_.prev(); + + index_++; + } + + return (keys_, values_); + } + function _descComparator(bytes32 key1_, bytes32 key2_) private pure returns (int256) { if (key1_ > key2_) { return -1; diff --git a/test/libs/data-structures/AvlTree.test.ts b/test/libs/data-structures/AvlTree.test.ts index 17bfbe35..51847075 100644 --- a/test/libs/data-structures/AvlTree.test.ts +++ b/test/libs/data-structures/AvlTree.test.ts @@ -1,10 +1,13 @@ import { ethers } from "hardhat"; +import { encodeBytes32String } from "ethers"; import { expect } from "chai"; import { Reverter } from "@/test/helpers/reverter"; + +import { SignerWithAddress } from "@nomicfoundation/hardhat-ethers/signers"; + import { AvlTreeMock } from "@ethers-v6"; + import { ZERO_ADDR } from "@/scripts/utils/constants"; -import { SignerWithAddress } from "@nomicfoundation/hardhat-ethers/signers"; -import { encodeBytes32String } from "ethers"; describe("AvlTree", () => { const reverter = new Reverter(); @@ -327,10 +330,6 @@ describe("AvlTree", () => { expect(fullTraversal[0]).to.deep.equal([1, 2, 4, 6, 7]); expect(fullTraversal[1]).to.deep.equal([12, 10, 11, 13, 14]); - let firstThreeTraversal = await avlTree.traverseFirstThreeUint(); - expect(firstThreeTraversal[0]).to.deep.equal([1, 2, 4]); - expect(firstThreeTraversal[1]).to.deep.equal([12, 10, 11]); - let backwardsTraversal = await avlTree.backwardsTraversalUint(); expect(backwardsTraversal[0]).to.deep.equal([7, 6, 4, 2, 1]); expect(backwardsTraversal[1]).to.deep.equal([14, 13, 11, 10, 12]); From 64d97dacb4d7348a13bea9697010a9006db022d7 Mon Sep 17 00:00:00 2001 From: Artem Chystiakov Date: Sun, 9 Jun 2024 15:04:52 +0300 Subject: [PATCH 10/11] version --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index be4355fa..46ec3008 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@solarity/solidity-lib", - "version": "2.7.6", + "version": "2.7.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@solarity/solidity-lib", - "version": "2.7.6", + "version": "2.7.7", "license": "MIT", "dependencies": { "@openzeppelin/contracts": "4.9.5", diff --git a/package.json b/package.json index f9d25cdb..b0b52671 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@solarity/solidity-lib", - "version": "2.7.6", + "version": "2.7.7", "license": "MIT", "author": "Distributed Lab", "readme": "README.md", From ab4b75d6fddae7efa7fa41f3a0abedeccfed48fb Mon Sep 17 00:00:00 2001 From: Artem Chystiakov Date: Mon, 10 Jun 2024 12:50:12 +0300 Subject: [PATCH 11/11] quick readme update --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8c3365f1..9365df3a 100644 --- a/README.md +++ b/README.md @@ -12,11 +12,12 @@ The library consists of modules and utilities that are built with a help of [Ope - Implementation of [**Contracts Registry**](https://eips.ethereum.org/EIPS/eip-6224) pattern - Versatile access control smart contracts (Merkle whitelists, RBAC) - Enhanced and simplified [**Diamond**](https://eips.ethereum.org/EIPS/eip-2535) pattern -- Advanced data structures (**Vector**, **PriorityQueue**) +- Advanced data structures (**Vector**, **PriorityQueue**, **AVLTree**) - ZK-friendly [**Sparse Merkle Tree**](https://docs.iden3.io/publications/pdfs/Merkle-Tree.pdf) and [**Incremental Merkle Tree**](https://github.com/runtimeverification/deposit-contract-verification/blob/master/deposit-contract-verification.pdf) implementations - Flexible finance primitives (**Staking**, **Vesting**) - Robust UniswapV2 and UniswapV3 oracles -- Utilities to ease work with ERC20 decimals, arrays, sets and ZK proofs +- Lightweight SBT implementation +- Utilities to ease work with memory, ERC20 decimals, arrays, sets and ZK proofs ## Overview