@@ -111,6 +111,7 @@ fn calculate_percentile(array: &mut [f64], percentile: f64) -> Result<f64, Dispe
111111 Ok ( ( h - h_floor as f64 ) * ( array[ h_floor] - array[ h_floor - 1 ] ) + array[ h_floor - 1 ] )
112112 }
113113}
114+
114115pub fn percentile ( rb_self : RArray , percentile : f64 ) -> Result < f64 , DispersionError > {
115116 let mut array = rb_self
116117 . to_vec :: < f64 > ( )
@@ -126,3 +127,193 @@ pub fn percentile(rb_self: RArray, percentile: f64) -> Result<f64, DispersionErr
126127
127128 calculate_percentile ( & mut array, percentile)
128129}
130+
131+ #[ cfg( test) ]
132+ mod tests {
133+ use super :: * ;
134+
135+ #[ test]
136+ fn test_calculate_mean ( ) {
137+ assert_eq ! ( calculate_mean( & [ 1.0 , 2.0 , 3.0 , 4.0 , 5.0 ] ) , 3.0 ) ;
138+ assert_eq ! ( calculate_mean( & [ 10.0 ] ) , 10.0 ) ;
139+ assert_eq ! ( calculate_mean( & [ 1.5 , 2.5 ] ) , 2.0 ) ;
140+ assert_eq ! ( calculate_mean( & [ -1.0 , 0.0 , 1.0 ] ) , 0.0 ) ;
141+ assert_eq ! ( calculate_mean( & [ 2.5 , 7.5 , 15.0 , 5.0 ] ) , 7.5 ) ;
142+ }
143+
144+ #[ test]
145+ fn test_distance_from_mean ( ) {
146+ // For [1, 2, 3], mean = 2, distances = [1, 0, 1], sum of squares = 2
147+ assert_eq ! ( distance_from_mean( & [ 1.0 , 2.0 , 3.0 ] ) , 2.0 ) ;
148+
149+ // For single element, distance should be 0
150+ assert_eq ! ( distance_from_mean( & [ 5.0 ] ) , 0.0 ) ;
151+
152+ // For [0, 0, 0], all distances are 0
153+ assert_eq ! ( distance_from_mean( & [ 0.0 , 0.0 , 0.0 ] ) , 0.0 ) ;
154+
155+ // For [-2, 0, 2], mean = 0, distances = [4, 0, 4], sum = 8
156+ assert_eq ! ( distance_from_mean( & [ -2.0 , 0.0 , 2.0 ] ) , 8.0 ) ;
157+ }
158+
159+ #[ test]
160+ fn test_calculate_variance_sample ( ) {
161+ // Sample variance: divide by n-1
162+ let data = [ 1.0 , 2.0 , 3.0 , 4.0 , 5.0 ] ;
163+ let expected = 2.5 ; // distance_from_mean = 10, n-1 = 4, variance = 2.5
164+ assert_eq ! ( calculate_variance( & data, false ) , expected) ;
165+
166+ // Two elements
167+ let data = [ 1.0 , 3.0 ] ;
168+ let expected = 2.0 ; // distance_from_mean = 2, n-1 = 1, variance = 2.0
169+ assert_eq ! ( calculate_variance( & data, false ) , expected) ;
170+
171+ // All same values
172+ let data = [ 5.0 , 5.0 , 5.0 ] ;
173+ assert_eq ! ( calculate_variance( & data, false ) , 0.0 ) ;
174+ }
175+
176+ #[ test]
177+ fn test_calculate_variance_population ( ) {
178+ // Population variance: divide by n
179+ let data = [ 1.0 , 2.0 , 3.0 , 4.0 , 5.0 ] ;
180+ let expected = 2.0 ; // distance_from_mean = 10, n = 5, variance = 2.0
181+ assert_eq ! ( calculate_variance( & data, true ) , expected) ;
182+
183+ // Single element
184+ let data = [ 10.0 ] ;
185+ assert_eq ! ( calculate_variance( & data, true ) , 0.0 ) ;
186+
187+ // Two elements
188+ let data = [ 1.0 , 3.0 ] ;
189+ let expected = 1.0 ; // distance_from_mean = 2, n = 2, variance = 1.0
190+ assert_eq ! ( calculate_variance( & data, true ) , expected) ;
191+ }
192+
193+ #[ test]
194+ fn test_calculate_stdev_sample ( ) {
195+ // Sample standard deviation is sqrt of sample variance
196+ let data = [ 1.0 , 2.0 , 3.0 , 4.0 , 5.0 ] ;
197+ let expected = 2.5_f64 . sqrt ( ) ; // sample variance = 2.5
198+ assert_eq ! ( calculate_stdev( & data, false ) , expected) ;
199+
200+ // All same values
201+ let data = [ 7.0 , 7.0 , 7.0 , 7.0 ] ;
202+ assert_eq ! ( calculate_stdev( & data, false ) , 0.0 ) ;
203+ }
204+
205+ #[ test]
206+ fn test_calculate_stdev_population ( ) {
207+ // Population standard deviation is sqrt of population variance
208+ let data = [ 1.0 , 2.0 , 3.0 , 4.0 , 5.0 ] ;
209+ let expected = 2.0_f64 . sqrt ( ) ; // population variance = 2.0
210+ assert_eq ! ( calculate_stdev( & data, true ) , expected) ;
211+
212+ // Single element
213+ let data = [ 42.0 ] ;
214+ assert_eq ! ( calculate_stdev( & data, true ) , 0.0 ) ;
215+ }
216+
217+ #[ test]
218+ fn test_calculate_percentile_basic ( ) {
219+ let mut data = [ 1.0 , 2.0 , 3.0 , 4.0 , 5.0 ] ;
220+
221+ // 0th percentile (minimum)
222+ assert_eq ! ( calculate_percentile( & mut data, 0.0 ) . unwrap( ) , 1.0 ) ;
223+
224+ // 50th percentile (median)
225+ assert_eq ! ( calculate_percentile( & mut data, 0.5 ) . unwrap( ) , 3.0 ) ;
226+
227+ // 100th percentile (maximum)
228+ assert_eq ! ( calculate_percentile( & mut data, 1.0 ) . unwrap( ) , 5.0 ) ;
229+ }
230+
231+ #[ test]
232+ fn test_calculate_percentile_interpolation ( ) {
233+ let mut data = [ 1.0 , 2.0 , 3.0 , 4.0 ] ;
234+
235+ // 25th percentile: h = (4-1)*0.25 + 1 = 1.75
236+ // Interpolate between index 0 (value 1) and index 1 (value 2)
237+ // Result = 0.75 * (2-1) + 1 = 1.75
238+ assert_eq ! ( calculate_percentile( & mut data, 0.25 ) . unwrap( ) , 1.75 ) ;
239+
240+ // 75th percentile: h = (4-1)*0.75 + 1 = 3.25
241+ // Interpolate between index 2 (value 3) and index 3 (value 4)
242+ // Result = 0.25 * (4-3) + 3 = 3.25
243+ assert_eq ! ( calculate_percentile( & mut data, 0.75 ) . unwrap( ) , 3.25 ) ;
244+ }
245+
246+ #[ test]
247+ fn test_calculate_percentile_single_element ( ) {
248+ let mut data = [ 42.0 ] ;
249+
250+ assert_eq ! ( calculate_percentile( & mut data, 0.0 ) . unwrap( ) , 42.0 ) ;
251+ assert_eq ! ( calculate_percentile( & mut data, 0.5 ) . unwrap( ) , 42.0 ) ;
252+ assert_eq ! ( calculate_percentile( & mut data, 1.0 ) . unwrap( ) , 42.0 ) ;
253+ }
254+
255+ #[ test]
256+ fn test_calculate_percentile_unsorted_data ( ) {
257+ let mut data = [ 5.0 , 1.0 , 3.0 , 2.0 , 4.0 ] ;
258+
259+ // Should sort internally and return correct percentiles
260+ assert_eq ! ( calculate_percentile( & mut data, 0.0 ) . unwrap( ) , 1.0 ) ;
261+ assert_eq ! ( calculate_percentile( & mut data, 0.5 ) . unwrap( ) , 3.0 ) ;
262+ assert_eq ! ( calculate_percentile( & mut data, 1.0 ) . unwrap( ) , 5.0 ) ;
263+ }
264+
265+ #[ test]
266+ fn test_calculate_percentile_with_duplicates ( ) {
267+ let mut data = [ 1.0 , 2.0 , 2.0 , 3.0 , 4.0 ] ;
268+
269+ assert_eq ! ( calculate_percentile( & mut data, 0.0 ) . unwrap( ) , 1.0 ) ;
270+ assert_eq ! ( calculate_percentile( & mut data, 1.0 ) . unwrap( ) , 4.0 ) ;
271+
272+ // 50th percentile should handle duplicates correctly
273+ let result = calculate_percentile ( & mut data, 0.5 ) . unwrap ( ) ;
274+ assert ! ( result >= 2.0 && result <= 3.0 ) ;
275+ }
276+
277+ #[ test]
278+ fn test_calculate_percentile_negative_numbers ( ) {
279+ let mut data = [ -5.0 , -2.0 , 0.0 , 2.0 , 5.0 ] ;
280+
281+ assert_eq ! ( calculate_percentile( & mut data, 0.0 ) . unwrap( ) , -5.0 ) ;
282+ assert_eq ! ( calculate_percentile( & mut data, 0.5 ) . unwrap( ) , 0.0 ) ;
283+ assert_eq ! ( calculate_percentile( & mut data, 1.0 ) . unwrap( ) , 5.0 ) ;
284+ }
285+
286+ #[ test]
287+ fn test_variance_and_stdev_consistency ( ) {
288+ let data = [ 1.0 , 2.0 , 3.0 , 4.0 , 5.0 ] ;
289+
290+ // Sample variance and stdev should be consistent
291+ let sample_var = calculate_variance ( & data, false ) ;
292+ let sample_stdev = calculate_stdev ( & data, false ) ;
293+ assert ! ( ( sample_stdev * sample_stdev - sample_var) . abs( ) < 1e-14 ) ;
294+
295+ // Population variance and stdev should be consistent
296+ let pop_var = calculate_variance ( & data, true ) ;
297+ let pop_stdev = calculate_stdev ( & data, true ) ;
298+ assert ! ( ( pop_stdev * pop_stdev - pop_var) . abs( ) < 1e-14 ) ;
299+ }
300+
301+ #[ test]
302+ fn test_mathematical_properties ( ) {
303+ let data = [ 2.0 , 4.0 , 6.0 , 8.0 , 10.0 ] ;
304+ let mean = calculate_mean ( & data) ;
305+
306+ // Mean should be the average
307+ assert_eq ! ( mean, 6.0 ) ;
308+
309+ // Population variance should be less than sample variance (when n > 1)
310+ let pop_var = calculate_variance ( & data, true ) ;
311+ let sample_var = calculate_variance ( & data, false ) ;
312+ assert ! ( pop_var < sample_var) ;
313+
314+ // Population stdev should be less than sample stdev
315+ let pop_stdev = calculate_stdev ( & data, true ) ;
316+ let sample_stdev = calculate_stdev ( & data, false ) ;
317+ assert ! ( pop_stdev < sample_stdev) ;
318+ }
319+ }
0 commit comments