ruby_native_statistics 1.1.0 → 2.0.0.rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,319 @@
1
+ use magnus::{Error, RArray, Ruby};
2
+
3
+ #[derive(thiserror::Error, Debug)]
4
+ pub enum DispersionError {
5
+ #[error("Array must have at least one element")]
6
+ EmptyArray,
7
+
8
+ #[error("Input is out of range")]
9
+ RangeError,
10
+
11
+ #[error("Magnus error")]
12
+ MagnusError(magnus::Error),
13
+ }
14
+
15
+ impl magnus::error::IntoError for DispersionError {
16
+ fn into_error(self, ruby: &Ruby) -> Error {
17
+ match self {
18
+ DispersionError::EmptyArray => {
19
+ Error::new(ruby.exception_range_error(), self.to_string())
20
+ }
21
+ DispersionError::RangeError => {
22
+ Error::new(ruby.exception_range_error(), self.to_string())
23
+ }
24
+ DispersionError::MagnusError(err) => err,
25
+ }
26
+ }
27
+ }
28
+
29
+ fn calculate_mean(array: &[f64]) -> f64 {
30
+ let length = array.len() as f64;
31
+ let sum = array.iter().sum::<f64>();
32
+ sum / length
33
+ }
34
+
35
+ fn calculate_variance(array: &[f64], population: bool) -> f64 {
36
+ let length = array.len() as f64;
37
+ let distance_from_mean = distance_from_mean(array);
38
+ let divisor = if population { length } else { length - 1.0 };
39
+ distance_from_mean / divisor
40
+ }
41
+
42
+ fn calculate_stdev(array: &[f64], population: bool) -> f64 {
43
+ calculate_variance(array, population).sqrt()
44
+ }
45
+
46
+ fn distance_from_mean(array: &[f64]) -> f64 {
47
+ let mean = calculate_mean(array);
48
+
49
+ array.iter().fold(0.0, |acc, x| acc + (x - mean).powi(2))
50
+ }
51
+
52
+ pub fn var(rb_self: RArray) -> Result<f64, DispersionError> {
53
+ let array = rb_self
54
+ .to_vec::<f64>()
55
+ .map_err(|e| DispersionError::MagnusError(e))?;
56
+
57
+ if array.is_empty() {
58
+ return Err(DispersionError::EmptyArray);
59
+ }
60
+
61
+ Ok(calculate_variance(&array, false))
62
+ }
63
+
64
+ pub fn stdev(rb_self: RArray) -> Result<f64, DispersionError> {
65
+ let array = rb_self
66
+ .to_vec::<f64>()
67
+ .map_err(|e| DispersionError::MagnusError(e))?;
68
+
69
+ if array.is_empty() {
70
+ return Err(DispersionError::EmptyArray);
71
+ }
72
+
73
+ Ok(calculate_stdev(&array, false))
74
+ }
75
+
76
+ pub fn varp(rb_self: RArray) -> Result<f64, DispersionError> {
77
+ let array = rb_self
78
+ .to_vec::<f64>()
79
+ .map_err(|e| DispersionError::MagnusError(e))?;
80
+
81
+ if array.is_empty() {
82
+ return Err(DispersionError::EmptyArray);
83
+ }
84
+
85
+ Ok(calculate_variance(&array, true))
86
+ }
87
+
88
+ pub fn stdevp(rb_self: RArray) -> Result<f64, DispersionError> {
89
+ let array = rb_self
90
+ .to_vec::<f64>()
91
+ .map_err(|e| DispersionError::MagnusError(e))?;
92
+
93
+ if array.is_empty() {
94
+ return Err(DispersionError::EmptyArray);
95
+ }
96
+
97
+ Ok(calculate_stdev(&array, true))
98
+ }
99
+
100
+ fn calculate_percentile(array: &mut [f64], percentile: f64) -> Result<f64, DispersionError> {
101
+ let length = array.len() as f64;
102
+
103
+ array.sort_by(|a, b| a.total_cmp(b));
104
+
105
+ let h = (length - 1.0) * percentile + 1.0;
106
+ if h.trunc() == h {
107
+ Ok(array[(h as usize) - 1])
108
+ } else {
109
+ let h_floor = h.trunc() as usize;
110
+
111
+ Ok((h - h_floor as f64) * (array[h_floor] - array[h_floor - 1]) + array[h_floor - 1])
112
+ }
113
+ }
114
+
115
+ pub fn percentile(rb_self: RArray, percentile: f64) -> Result<f64, DispersionError> {
116
+ let mut array = rb_self
117
+ .to_vec::<f64>()
118
+ .map_err(|e| DispersionError::MagnusError(e))?;
119
+
120
+ if array.is_empty() {
121
+ return Err(DispersionError::EmptyArray);
122
+ }
123
+
124
+ if !(0.0..=1.0).contains(&percentile) {
125
+ return Err(DispersionError::RangeError);
126
+ }
127
+
128
+ calculate_percentile(&mut array, percentile)
129
+ }
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,0 +1,20 @@
1
+ use magnus::{Error, Module, Ruby, method};
2
+
3
+ mod dispersion;
4
+ mod mathematics;
5
+
6
+ #[magnus::init]
7
+ fn init(ruby: &Ruby) -> Result<(), Error> {
8
+ let array = ruby.class_array();
9
+
10
+ array.define_method("mean", method!(mathematics::mean, 0))?;
11
+ array.define_method("median", method!(mathematics::median, 0))?;
12
+ array.define_method("stdev", method!(dispersion::stdev, 0))?;
13
+ array.define_method("stdevs", method!(dispersion::stdev, 0))?;
14
+ array.define_method("stdevp", method!(dispersion::stdevp, 0))?;
15
+ array.define_method("var", method!(dispersion::var, 0))?;
16
+ array.define_method("varp", method!(dispersion::varp, 0))?;
17
+ array.define_method("percentile", method!(dispersion::percentile, 1))?;
18
+
19
+ Ok(())
20
+ }
@@ -0,0 +1,255 @@
1
+ use magnus::{Error, RArray, Ruby};
2
+
3
+ #[derive(thiserror::Error, Debug)]
4
+ pub enum MathematicsError {
5
+ #[error("Array must have at least one element")]
6
+ EmptyArray,
7
+
8
+ #[error("Magnus error")]
9
+ MagnusError(magnus::Error),
10
+ }
11
+
12
+ impl magnus::error::IntoError for MathematicsError {
13
+ fn into_error(self, ruby: &Ruby) -> Error {
14
+ match self {
15
+ MathematicsError::EmptyArray => {
16
+ Error::new(ruby.exception_range_error(), self.to_string())
17
+ }
18
+ MathematicsError::MagnusError(err) => err,
19
+ }
20
+ }
21
+ }
22
+
23
+ pub fn calculate_mean(array: &[f64]) -> f64 {
24
+ let length = array.len() as f64;
25
+ let sum = array.iter().sum::<f64>();
26
+ sum / length
27
+ }
28
+
29
+ pub fn calculate_median(array: &[f64]) -> Result<f64, MathematicsError> {
30
+ if array.is_empty() {
31
+ return Err(MathematicsError::EmptyArray);
32
+ }
33
+
34
+ let mut sorted_array = array.to_vec();
35
+ sorted_array.sort_by(|a, b| a.total_cmp(b));
36
+
37
+ let length = sorted_array.len();
38
+ let array_even_size = (length % 2) == 0;
39
+ let middle = length / 2;
40
+
41
+ if !array_even_size {
42
+ Ok(sorted_array[middle])
43
+ } else {
44
+ Ok((sorted_array[middle - 1] + sorted_array[middle]) / 2.0)
45
+ }
46
+ }
47
+
48
+ pub fn mean(rb_self: RArray) -> Result<f64, MathematicsError> {
49
+ let array = rb_self
50
+ .to_vec::<f64>()
51
+ .map_err(|e| MathematicsError::MagnusError(e))?;
52
+
53
+ if array.is_empty() {
54
+ return Err(MathematicsError::EmptyArray);
55
+ }
56
+
57
+ Ok(calculate_mean(&array))
58
+ }
59
+
60
+ pub fn median(rb_self: RArray) -> Result<f64, MathematicsError> {
61
+ let array = rb_self
62
+ .to_vec::<f64>()
63
+ .map_err(|e| MathematicsError::MagnusError(e))?;
64
+
65
+ calculate_median(&array)
66
+ }
67
+
68
+ #[cfg(test)]
69
+ mod tests {
70
+ use super::*;
71
+
72
+ #[test]
73
+ fn test_calculate_mean_single_element() {
74
+ assert_eq!(calculate_mean(&[5.0]), 5.0);
75
+ }
76
+
77
+ #[test]
78
+ fn test_calculate_mean_multiple_elements() {
79
+ assert_eq!(calculate_mean(&[1.0, 2.0, 3.0, 4.0, 5.0]), 3.0);
80
+ }
81
+
82
+ #[test]
83
+ fn test_calculate_mean_with_negative_numbers() {
84
+ assert_eq!(calculate_mean(&[-2.0, -1.0, 0.0, 1.0, 2.0]), 0.0);
85
+ }
86
+
87
+ #[test]
88
+ fn test_calculate_mean_with_decimals() {
89
+ let result = calculate_mean(&[1.5, 2.5, 3.5]);
90
+ assert!((result - 2.5).abs() < f64::EPSILON);
91
+ }
92
+
93
+ #[test]
94
+ fn test_calculate_mean_large_numbers() {
95
+ let result = calculate_mean(&[1000000.0, 2000000.0, 3000000.0]);
96
+ assert_eq!(result, 2000000.0);
97
+ }
98
+
99
+ #[test]
100
+ fn test_calculate_median_empty_array() {
101
+ let result = calculate_median(&[]);
102
+ assert!(matches!(result, Err(MathematicsError::EmptyArray)));
103
+ }
104
+
105
+ #[test]
106
+ fn test_calculate_median_single_element() {
107
+ assert_eq!(calculate_median(&[42.0]).unwrap(), 42.0);
108
+ }
109
+
110
+ #[test]
111
+ fn test_calculate_median_odd_length_sorted() {
112
+ assert_eq!(calculate_median(&[1.0, 2.0, 3.0]).unwrap(), 2.0);
113
+ }
114
+
115
+ #[test]
116
+ fn test_calculate_median_odd_length_unsorted() {
117
+ assert_eq!(calculate_median(&[3.0, 1.0, 2.0]).unwrap(), 2.0);
118
+ }
119
+
120
+ #[test]
121
+ fn test_calculate_median_even_length_sorted() {
122
+ assert_eq!(calculate_median(&[1.0, 2.0, 3.0, 4.0]).unwrap(), 2.5);
123
+ }
124
+
125
+ #[test]
126
+ fn test_calculate_median_even_length_unsorted() {
127
+ assert_eq!(calculate_median(&[4.0, 1.0, 3.0, 2.0]).unwrap(), 2.5);
128
+ }
129
+
130
+ #[test]
131
+ fn test_calculate_median_with_duplicates() {
132
+ assert_eq!(calculate_median(&[1.0, 2.0, 2.0, 3.0]).unwrap(), 2.0);
133
+ assert_eq!(calculate_median(&[1.0, 2.0, 2.0, 2.0, 3.0]).unwrap(), 2.0);
134
+ }
135
+
136
+ #[test]
137
+ fn test_calculate_median_with_negative_numbers() {
138
+ assert_eq!(calculate_median(&[-3.0, -1.0, 0.0, 1.0, 3.0]).unwrap(), 0.0);
139
+ assert_eq!(calculate_median(&[-4.0, -2.0, -1.0, 1.0]).unwrap(), -1.5);
140
+ }
141
+
142
+ #[test]
143
+ fn test_calculate_median_with_decimals() {
144
+ let result = calculate_median(&[1.1, 2.2, 3.3]).unwrap();
145
+ assert!((result - 2.2).abs() < f64::EPSILON);
146
+ }
147
+
148
+ #[test]
149
+ fn test_calculate_median_preserves_original_array() {
150
+ let original = vec![3.0, 1.0, 4.0, 2.0];
151
+ let original_copy = original.clone();
152
+ let _result = calculate_median(&original).unwrap();
153
+ assert_eq!(original, original_copy);
154
+ }
155
+
156
+ #[test]
157
+ fn test_calculate_median_with_infinity() {
158
+ assert_eq!(
159
+ calculate_median(&[f64::NEG_INFINITY, 0.0, f64::INFINITY]).unwrap(),
160
+ 0.0
161
+ );
162
+ }
163
+
164
+ #[test]
165
+ fn test_calculate_median_with_nan() {
166
+ // NaN values should be handled by total_cmp, but behavior may be implementation-defined
167
+ let result = calculate_median(&[1.0, f64::NAN, 3.0]);
168
+ // We can't easily test the exact result with NaN, but it shouldn't panic
169
+ assert!(result.is_ok());
170
+ }
171
+
172
+ #[test]
173
+ fn test_mathematics_error_display() {
174
+ let empty_error = MathematicsError::EmptyArray;
175
+ assert_eq!(
176
+ empty_error.to_string(),
177
+ "Array must have at least one element"
178
+ );
179
+ }
180
+
181
+ #[test]
182
+ fn test_mathematics_error_debug() {
183
+ let empty_error = MathematicsError::EmptyArray;
184
+ let debug_string = format!("{:?}", empty_error);
185
+ assert!(debug_string.contains("EmptyArray"));
186
+ }
187
+
188
+ // Integration-style tests for the core functions
189
+ #[test]
190
+ fn test_mean_and_median_consistency_single_element() {
191
+ let data = [7.5];
192
+ let mean_result = calculate_mean(&data);
193
+ let median_result = calculate_median(&data).unwrap();
194
+ assert_eq!(mean_result, median_result);
195
+ }
196
+
197
+ #[test]
198
+ fn test_mean_and_median_symmetric_distribution() {
199
+ let data = [1.0, 2.0, 3.0, 4.0, 5.0];
200
+ let mean_result = calculate_mean(&data);
201
+ let median_result = calculate_median(&data).unwrap();
202
+ assert_eq!(mean_result, median_result);
203
+ }
204
+
205
+ #[test]
206
+ fn test_large_dataset_performance() {
207
+ let large_data: Vec<f64> = (1..=10000).map(|x| x as f64).collect();
208
+
209
+ let mean_result = calculate_mean(&large_data);
210
+ assert_eq!(mean_result, 5000.5);
211
+
212
+ let median_result = calculate_median(&large_data).unwrap();
213
+ assert_eq!(median_result, 5000.5);
214
+ }
215
+
216
+ #[test]
217
+ fn test_edge_case_very_small_numbers() {
218
+ let data = [
219
+ f64::MIN_POSITIVE,
220
+ f64::MIN_POSITIVE * 2.0,
221
+ f64::MIN_POSITIVE * 3.0,
222
+ ];
223
+ let mean_result = calculate_mean(&data);
224
+ let median_result = calculate_median(&data).unwrap();
225
+
226
+ assert!(mean_result > 0.0);
227
+ assert!(median_result > 0.0);
228
+ }
229
+
230
+ #[test]
231
+ fn test_edge_case_very_large_numbers() {
232
+ let large_value = f64::MAX / 10.0;
233
+ let data = [large_value, large_value, large_value];
234
+ let mean_result = calculate_mean(&data);
235
+ let median_result = calculate_median(&data).unwrap();
236
+
237
+ assert!(!mean_result.is_infinite());
238
+ assert!(!median_result.is_infinite());
239
+ assert_eq!(mean_result, median_result);
240
+ assert_eq!(mean_result, large_value);
241
+ }
242
+
243
+ #[test]
244
+ fn test_precision_with_many_small_values() {
245
+ let data: Vec<f64> = vec![0.1; 1000];
246
+
247
+ let median_result = calculate_median(&data).unwrap();
248
+ assert!((median_result - 0.1).abs() < f64::EPSILON);
249
+
250
+ let mean_result = calculate_mean(&data);
251
+ // Use a reasonable tolerance for accumulated floating point errors
252
+ let tolerance = 1e-10;
253
+ assert!((mean_result - 0.1).abs() < tolerance);
254
+ }
255
+ }
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RubyNativeStatistics
2
- VERSION = "1.1.0"
4
+ VERSION = '2.0.0.rc.1'
3
5
  end
@@ -1,7 +1,4 @@
1
- require "ruby_native_statistics/version"
2
- require "ruby_native_statistics/ruby_native_statistics"
1
+ # frozen_string_literal: true
3
2
 
4
- class Array
5
- include Mathematics
6
- include Dispersion
7
- end
3
+ require 'ruby_native_statistics/version'
4
+ require 'ruby_native_statistics/ruby_native_statistics'