Skip to content

Commit b30c92b

Browse files
authored
feature/prefix-sum (#30)
* init prefix sum lib * change version * moved prefix sum lib to array helper lib * typos * added prefix sum methods to mock * added range check & coverage 100% * typo * rm range checks
1 parent 2c39e95 commit b30c92b

File tree

4 files changed

+97
-1
lines changed

4 files changed

+97
-1
lines changed

contracts/contracts-registry/AbstractContractsRegistry.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ abstract contract AbstractContractsRegistry is Initializable {
6565
}
6666

6767
/**
68-
* @notice The function that check if a contract with a given name has been added
68+
* @notice The function that checks if a contract with a given name has been added
6969
* @param name_ the name of the contract
7070
* @return true if the contract is present in the registry
7171
*/

contracts/libs/arrays/ArrayHelper.sol

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,4 +102,45 @@ library ArrayHelper {
102102
array_ = new string[](1);
103103
array_[0] = elem_;
104104
}
105+
106+
/**
107+
* @notice The function to compute the prefix sum array
108+
* @param arr_ the initial array to be turned into the prefix sum array
109+
* @return prefixes_ the prefix sum array
110+
*/
111+
function countPrefixes(
112+
uint256[] memory arr_
113+
) internal pure returns (uint256[] memory prefixes_) {
114+
if (arr_.length == 0) {
115+
return prefixes_;
116+
}
117+
118+
prefixes_ = new uint256[](arr_.length);
119+
prefixes_[0] = arr_[0];
120+
121+
for (uint256 i = 1; i < prefixes_.length; i++) {
122+
prefixes_[i] = prefixes_[i - 1] + arr_[i];
123+
}
124+
}
125+
126+
/**
127+
* @notice The function that calculates the sum of all array elements from `beginIndex_` to
128+
* `endIndex_` inclusive using its prefix sum array
129+
* @param beginIndex_ the index of the first range element
130+
* @param endIndex_ the index of the last range element
131+
* @return the sum of all elements of the range
132+
*/
133+
function getRangeSum(
134+
uint256[] memory prefixes_,
135+
uint256 beginIndex_,
136+
uint256 endIndex_
137+
) internal pure returns (uint256) {
138+
require(beginIndex_ <= endIndex_, "ArrayHelper: wrong range");
139+
140+
if (beginIndex_ == 0) {
141+
return prefixes_[endIndex_];
142+
}
143+
144+
return prefixes_[endIndex_] - prefixes_[beginIndex_ - 1];
145+
}
105146
}

contracts/mock/libs/arrays/ArrayHelperMock.sol

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,16 @@ contract ArrayHelperMock {
5353
function asArrayString(string memory elem_) external pure returns (string[] memory array_) {
5454
return elem_.asArray();
5555
}
56+
57+
function countPrefixes(uint256[] memory arr_) external pure returns (uint256[] memory) {
58+
return arr_.countPrefixes();
59+
}
60+
61+
function getRangeSum(
62+
uint256[] memory arr_,
63+
uint256 beginIndex_,
64+
uint256 endIndex_
65+
) external pure returns (uint256) {
66+
return arr_.countPrefixes().getRangeSum(beginIndex_, endIndex_);
67+
}
5668
}

test/libs/arrays/ArrayHelper.test.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,4 +117,47 @@ describe("ArrayHelperMock", () => {
117117
assert.deepEqual(await mock.asArrayString("1"), ["1"]);
118118
});
119119
});
120+
121+
describe("prefix sum array", () => {
122+
function getArraySum(arr) {
123+
return arr.reduce((prev, cur) => prev + cur, 0);
124+
}
125+
126+
function countPrefixes(arr) {
127+
return arr.map((e, idx) => getArraySum(arr.slice(0, idx + 1)));
128+
}
129+
130+
const array = [0, 100, 50, 4, 34, 520, 4];
131+
132+
describe("countPrefixes", () => {
133+
it("should compute prefix array properly", async () => {
134+
assert.deepEqual(await mock.countPrefixes([]), []);
135+
assert.deepEqual(
136+
(await mock.countPrefixes(array)).map((e) => e.toNumber()),
137+
countPrefixes(array)
138+
);
139+
});
140+
});
141+
142+
describe("getRangeSum", () => {
143+
it("should get the range sum properly if all conditions are met", async () => {
144+
for (let l = 0; l < array.length; l++) {
145+
for (let r = l; r < array.length; r++) {
146+
assert.equal((await mock.getRangeSum(array, l, r)).toNumber(), getArraySum(array.slice(l, r + 1)));
147+
}
148+
}
149+
});
150+
151+
it("should revert if the first index is greater than the last one", async () => {
152+
await truffleAssert.reverts(mock.getRangeSum(array, array.length - 1, 0), "ArrayHelper: wrong range");
153+
await truffleAssert.reverts(mock.getRangeSum(array, 1, 0), "ArrayHelper: wrong range");
154+
await truffleAssert.reverts(mock.getRangeSum(array, 2, 1), "ArrayHelper: wrong range");
155+
});
156+
157+
it("should revert if one of the indexes is out of range", async () => {
158+
await truffleAssert.reverts(mock.getRangeSum([], 0, 0));
159+
await truffleAssert.reverts(mock.getRangeSum(array, 0, array.length));
160+
});
161+
});
162+
});
120163
});

0 commit comments

Comments
 (0)