Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bound lookup in arrays with duplicate #4842

Merged
merged 13 commits into from
Jan 29, 2024
51 changes: 17 additions & 34 deletions contracts/utils/Arrays.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,12 @@ library Arrays {
* values in the array are strictly less than `element`), the array length is
* returned. Time complexity O(log n).
*
* `array` is expected to be sorted in ascending order, and to contain no
* repeated elements.
* NOTE: The `array` is expected to be sorted in ascending order, and to
* contain no repeated elements.
*
* Deprecated in favor of `lowerBound` and `findUpperBound`. Note that this actually implements a lower bound
* search, and should be replaced with `lowerBound`
* IMPORTANT: Deprecated. This implementation behaves as {lowerBound} but lacks
* support for repeated elements in the array. The {lowerBound} function should
* be used instead.
*/
function findUpperBound(uint256[] storage array, uint256 element) internal view returns (uint256) {
uint256 low = 0;
Expand Down Expand Up @@ -53,14 +54,12 @@ library Arrays {
}

/**
* @dev Searches a sorted `array` and returns the first index that contains
* a value greater or equal to `element`. If no such index exists (i.e. all
* values in the array are strictly less than `element`), the array length is
* returned. Time complexity O(log n).
*
* `array` is expected to be sorted in ascending order,
* @dev Searches an `array` sorted in ascending order and returns the first
* index that contains a value greater or equal than `element`. If no such index
* exists (i.e. all values in the array are strictly less than `element`), the array
* length is returned. Time complexity O(log n).
*
* See https://en.cppreference.com/w/cpp/algorithm/ranges/lower_bound
* See C++'s https://en.cppreference.com/w/cpp/algorithm/lower_bound[lower_bound]
*/
function lowerBound(uint256[] storage array, uint256 element) internal view returns (uint256) {
uint256 low = 0;
Expand Down Expand Up @@ -89,14 +88,12 @@ library Arrays {
}

/**
* @dev Searches a sorted `array` and returns the first index that contains
* a value strictly greater to `element`. If no such index exists (i.e. all
* values in the array are strictly less than `element`), the array length is
* returned. Time complexity O(log n).
* @dev Searches an `array` sorted in ascending order and returns the first
* index that contains a value strictly greater than `element`. If no such index
* exists (i.e. all values in the array are strictly less than `element`), the array
* length is returned. Time complexity O(log n).
*
* `array` is expected to be sorted in ascending order,
*
* See https://en.cppreference.com/w/cpp/algorithm/upper_bound
* See C++'s https://en.cppreference.com/w/cpp/algorithm/upper_bound[upper_bound]
*/
function upperBound(uint256[] storage array, uint256 element) internal view returns (uint256) {
uint256 low = 0;
Expand Down Expand Up @@ -125,14 +122,7 @@ library Arrays {
}

/**
* @dev Searches a sorted `array` and returns the first index that contains
* a value greater or equal to `element`. If no such index exists (i.e. all
* values in the array are strictly less than `element`), the array length is
* returned. Time complexity O(log n).
*
* `array` is expected to be sorted in ascending order,
*
* See https://en.cppreference.com/w/cpp/algorithm/ranges/lower_bound
* @dev Same as {lowerBound}, but with an array in memory.
*/
function lowerBoundMemory(uint256[] memory array, uint256 element) internal pure returns (uint256) {
uint256 low = 0;
Expand Down Expand Up @@ -161,14 +151,7 @@ library Arrays {
}

/**
* @dev Searches a sorted `array` and returns the first index that contains
* a value strictly greater to `element`. If no such index exists (i.e. all
* values in the array are strictly less than `element`), the array length is
* returned. Time complexity O(log n).
*
* `array` is expected to be sorted in ascending order,
*
* See https://en.cppreference.com/w/cpp/algorithm/upper_bound
* @dev Same as {upperBound}, but with an array in memory.
*/
function upperBoundMemory(uint256[] memory array, uint256 element) internal pure returns (uint256) {
uint256 low = 0;
Expand Down
7 changes: 5 additions & 2 deletions test/utils/Arrays.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,11 @@ describe('Arrays', function () {
describe(name, function () {
it('[deprecated] findUpperBound', async function () {
// findUpperBound does not support duplicated
if (hasDuplicates(array)) this.skip();
expect(await this.mock.findUpperBound(input)).to.be.equal(lowerBound(array, input));
if (hasDuplicates(array)) {
expect(await this.mock.findUpperBound(input)).to.be.equal(upperBound(array, input) - 1);
Amxx marked this conversation as resolved.
Show resolved Hide resolved
} else {
expect(await this.mock.findUpperBound(input)).to.be.equal(lowerBound(array, input));
}
});

it('lowerBound', async function () {
Expand Down
Loading