@@ -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,139 @@ 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 array bounds
177+ function testFuzz_ArrayBounds (uint8 length ) external pure {
178+ // limit array size to prevent overflow and excessive gas costs
179+ vm.assume (length > 0 && length <= 32 );
180+
181+ uint256 [] memory data = new uint256 [](length);
182+ for (uint256 i = 0 ; i < length; i++ ) {
183+ data[i] = MIN_SCORE; // use min to avoid overflow
184+ }
185+
186+ uint256 avg = Statistics.avg (data);
187+ assertEq (avg, MIN_SCORE * 1 ether);
188+
189+ (uint256 variance ,) = Statistics.variance (data);
190+ assertEq (variance, 0 );
191+ }
192+
193+ // Test invariance under translation
194+ function testFuzz_TranslationInvariance (uint8 shift ) external pure {
195+ vm.assume (shift <= 50 ); // Ensure we don't overflow MAX_SCORE
196+
197+ uint256 [] memory data10 = new uint256 [](3 );
198+ uint256 [] memory data20 = new uint256 [](3 );
199+
200+ // original data
201+ data10[0 ] = 100 ;
202+ data10[1 ] = 150 ;
203+ data10[2 ] = 200 ;
204+
205+ // shifted data
206+ data20[0 ] = 100 + shift;
207+ data20[1 ] = 150 + shift;
208+ data20[2 ] = 200 + shift;
209+
210+ (uint256 variance1 ,) = Statistics.variance (data10);
211+ (uint256 variance2 ,) = Statistics.variance (data20);
212+
213+ // variance should be invariant under translation
214+ assertApproxEqAbs (variance1, variance2, 1e15 );
215+ }
216+
217+ // Test that variance scales correctly when data is multiplied
218+ function testFuzz_ScaleInvariance (uint8 length , uint8 scale ) external pure {
219+ vm.assume (length > 0 && length <= 32 );
220+ vm.assume (scale > 0 && scale <= 10 );
221+
222+ uint256 [] memory data = new uint256 [](length);
223+ uint256 [] memory scaledData = new uint256 [](length);
224+
225+ for (uint256 i = 0 ; i < length; i++ ) {
226+ data[i] = bound (uint256 (uint256 (keccak256 (abi.encode (i))) % MAX_SCORE), MIN_SCORE, MAX_SCORE / scale);
227+ scaledData[i] = data[i] * scale;
228+ }
229+
230+ (uint256 variance1 ,) = Statistics.variance (data);
231+ (uint256 variance2 ,) = Statistics.variance (scaledData);
232+
233+ assertApproxEqAbs (variance2, variance1 * scale * scale, 1e15 );
234+ }
235+
236+ function testFuzz_OrderInvariance (uint8 length ) external view {
237+ vm.assume (length > 1 && length <= 32 );
238+
239+ uint256 [] memory data = new uint256 [](length);
240+ uint256 [] memory shuffledData = new uint256 [](length);
241+
242+ for (uint256 i = 0 ; i < length; i++ ) {
243+ data[i] = bound (uint256 (uint256 (keccak256 (abi.encode (i))) % MAX_SCORE), MIN_SCORE, MAX_SCORE);
244+ shuffledData[i] = data[i];
245+ }
246+
247+ // Shuffle
248+ for (uint256 i = length - 1 ; i > 0 ; i-- ) {
249+ uint256 j = uint256 (keccak256 (abi.encodePacked (block .timestamp , i))) % (i + 1 );
250+ (shuffledData[i], shuffledData[j]) = (shuffledData[j], shuffledData[i]);
251+ }
252+
253+ uint256 avg1 = Statistics.avg (data);
254+ uint256 avg2 = Statistics.avg (shuffledData);
255+ (uint256 variance1 ,) = Statistics.variance (data);
256+ (uint256 variance2 ,) = Statistics.variance (shuffledData);
257+
258+ assertEq (avg1, avg2);
259+ assertEq (variance1, variance2);
260+ }
261+
262+ function testFuzz_ExtremeValues (uint8 length ) external pure {
263+ vm.assume (length > 1 && length <= 32 ); // Must have at least 2 elements
264+
265+ uint256 [] memory extremeData = new uint256 [](length);
266+ for (uint256 i = 0 ; i < length; i++ ) {
267+ extremeData[i] = i % 2 == 0 ? MAX_SCORE : MIN_SCORE;
268+ }
269+
270+ (uint256 variance ,) = Statistics.variance (extremeData);
271+ assert (variance > 0 );
176272 }
177273}
0 commit comments