@@ -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