Skip to content

Commit 81c1556

Browse files
committed
Statistics.t.sol: Add statistic fuzz tests
1 parent c01311d commit 81c1556

File tree

1 file changed

+118
-32
lines changed

1 file changed

+118
-32
lines changed

test/Statistics.t.sol

Lines changed: 118 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,9 @@ contract StatisticsTest is Test {
112112
data[3] = number4;
113113

114114
uint256 average = Statistics.avg(data);
115-
// console.log("Average: ", average);
115+
116+
assert(average >= Math.min(Math.min(number1, number2), Math.min(number3, number4)) * 1 ether);
117+
assert(average <= Math.max(Math.max(number1, number2), Math.max(number3, number4)) * 1 ether);
116118
}
117119

118120
function testFuzz_Variance(uint8 number1, uint8 number2, uint8 number3, uint8 number4, uint8 number5)
@@ -133,45 +135,129 @@ contract StatisticsTest is Test {
133135
data[4] = number5;
134136

135137
(uint256 variance,) = Statistics.variance(data);
136-
// console.log("Variance: ", variance);
138+
139+
// variance be 0 if all numbers are equal
140+
if (number1 == number2 && number2 == number3 && number3 == number4 && number4 == number5) {
141+
assertEq(variance, 0);
142+
}
143+
144+
// variance should be non-negative
145+
assert(variance >= 0);
137146
}
138147

139-
function testFuzz_StandardDeviation(
140-
uint8 number1,
141-
uint8 number2,
142-
uint8 number3,
143-
uint8 number4,
144-
uint8 number5,
145-
uint8 number6,
146-
uint8 number7,
147-
uint8 number8,
148-
uint8 number9,
149-
uint8 number10
150-
) external pure {
151-
vm.assume(number1 <= MAX_SCORE && number1 > MIN_SCORE);
152-
vm.assume(number2 <= MAX_SCORE && number2 > MIN_SCORE);
153-
vm.assume(number3 <= MAX_SCORE && number3 > MIN_SCORE);
154-
vm.assume(number4 <= MAX_SCORE && number4 > MIN_SCORE);
155-
vm.assume(number5 <= MAX_SCORE && number5 > MIN_SCORE);
156-
vm.assume(number6 <= MAX_SCORE && number6 > MIN_SCORE);
157-
vm.assume(number7 <= MAX_SCORE && number7 > MIN_SCORE);
158-
vm.assume(number8 <= MAX_SCORE && number8 > MIN_SCORE);
159-
vm.assume(number9 <= MAX_SCORE && number9 > MIN_SCORE);
160-
vm.assume(number10 <= MAX_SCORE && number10 > MIN_SCORE);
161-
162-
uint256[] memory data = new uint256[](10);
148+
function testFuzz_StandardDeviation(uint8 number1, uint8 number2, uint8 number3, uint8 number4, uint8 number5)
149+
external
150+
pure
151+
{
152+
vm.assume(number1 <= MAX_SCORE && number1 >= MIN_SCORE);
153+
vm.assume(number2 <= MAX_SCORE && number2 >= MIN_SCORE);
154+
vm.assume(number3 <= MAX_SCORE && number3 >= MIN_SCORE);
155+
vm.assume(number4 <= MAX_SCORE && number4 >= MIN_SCORE);
156+
vm.assume(number5 <= MAX_SCORE && number5 >= MIN_SCORE);
157+
158+
uint256[] memory data = new uint256[](5);
163159
data[0] = number1;
164160
data[1] = number2;
165161
data[2] = number3;
166162
data[3] = number4;
167163
data[4] = number5;
168-
data[5] = number6;
169-
data[6] = number7;
170-
data[7] = number8;
171-
data[8] = number9;
172-
data[9] = number10;
173164

174165
(uint256 stddev,) = Statistics.stddev(data);
175-
// console.log("Standard Deviation: ", stddev);
166+
167+
// standard deviation should be 0 if all numbers are equal
168+
if (number1 == number2 && number2 == number3 && number3 == number4 && number4 == number5) {
169+
assertEq(stddev, 0);
170+
}
171+
172+
// standard deviation should be non-negative
173+
assert(stddev >= 0);
174+
}
175+
176+
// Test for empty array handling
177+
function test_EmptyArray() external {
178+
uint256[] memory emptyData = new uint256[](0);
179+
vm.expectRevert("Array is empty");
180+
Statistics.avg(emptyData);
181+
182+
vm.expectRevert("Array is empty");
183+
Statistics.variance(emptyData);
184+
185+
vm.expectRevert("Array is empty");
186+
Statistics.stddev(emptyData);
187+
}
188+
189+
// Test for single element array
190+
function test_SingleElement() external pure {
191+
uint256[] memory singleData = new uint256[](1);
192+
singleData[0] = 100;
193+
194+
uint256 avg = Statistics.avg(singleData);
195+
assertEq(avg, 100 ether);
196+
197+
(uint256 variance,) = Statistics.variance(singleData);
198+
assertEq(variance, 0);
199+
200+
(uint256 stddev,) = Statistics.stddev(singleData);
201+
assertEq(stddev, 0);
202+
}
203+
204+
// Test for array bounds
205+
function testFuzz_ArrayBounds(uint8 length) external pure {
206+
// limit array size to prevent overflow and excessive gas costs
207+
vm.assume(length > 0 && length <= 32);
208+
209+
uint256[] memory data = new uint256[](length);
210+
for (uint256 i = 0; i < length; i++) {
211+
data[i] = MIN_SCORE; // use min to avoid overflow
212+
}
213+
214+
uint256 avg = Statistics.avg(data);
215+
assertEq(avg, MIN_SCORE * 1 ether);
216+
217+
(uint256 variance,) = Statistics.variance(data);
218+
assertEq(variance, 0);
219+
}
220+
221+
// Test for statistical properties
222+
function test_StatisticalProperties() external pure {
223+
// test mean minimizes squared deviations
224+
uint256[] memory data = new uint256[](3);
225+
data[0] = 10;
226+
data[1] = 20;
227+
data[2] = 30;
228+
229+
uint256 mean = Statistics.avg(data);
230+
(uint256 variance, uint256 calculatedMean) = Statistics.variance(data);
231+
232+
// mean should match between avg() and variance()
233+
assertEq(mean, calculatedMean);
234+
235+
// variance should equal average of squared deviations
236+
uint256 expectedVariance = ((10 ether - mean) ** 2 + (20 ether - mean) ** 2 + (30 ether - mean) ** 2) / 3;
237+
assertApproxEqAbs(variance, expectedVariance, 1e15);
238+
}
239+
240+
// test invariance under translation
241+
function testFuzz_TranslationInvariance(uint8 shift) external pure {
242+
vm.assume(shift <= 50); // Ensure we don't overflow MAX_SCORE
243+
244+
uint256[] memory data10 = new uint256[](3);
245+
uint256[] memory data20 = new uint256[](3);
246+
247+
// original data
248+
data10[0] = 100;
249+
data10[1] = 150;
250+
data10[2] = 200;
251+
252+
// shifted data
253+
data20[0] = 100 + shift;
254+
data20[1] = 150 + shift;
255+
data20[2] = 200 + shift;
256+
257+
(uint256 variance1,) = Statistics.variance(data10);
258+
(uint256 variance2,) = Statistics.variance(data20);
259+
260+
// variance should be invariant under translation
261+
assertApproxEqAbs(variance1, variance2, 1e15);
176262
}
177263
}

0 commit comments

Comments
 (0)