1use num_traits::ToPrimitive;
2use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator};
3use rayon::prelude::ParallelSlice;
4use rayon::slice::ParallelSliceMut;
5
6use serde::{Deserialize, Serialize};
7
8use {crate::Commute, crate::Partial};
9
10const PARALLEL_THRESHOLD: usize = 10_000;
11
12pub fn median<I>(it: I) -> Option<f64>
16where
17 I: Iterator,
18 <I as Iterator>::Item: PartialOrd + ToPrimitive + Send,
19{
20 it.collect::<Unsorted<_>>().median()
21}
22
23pub fn mad<I>(it: I, precalc_median: Option<f64>) -> Option<f64>
25where
26 I: Iterator,
27 <I as Iterator>::Item: PartialOrd + ToPrimitive + Send + Sync,
28{
29 it.collect::<Unsorted<_>>().mad(precalc_median)
30}
31
32pub fn quartiles<I>(it: I) -> Option<(f64, f64, f64)>
36where
37 I: Iterator,
38 <I as Iterator>::Item: PartialOrd + ToPrimitive + Send,
39{
40 it.collect::<Unsorted<_>>().quartiles()
41}
42
43pub fn mode<T, I>(it: I) -> Option<T>
49where
50 T: PartialOrd + Clone + Send,
51 I: Iterator<Item = T>,
52{
53 it.collect::<Unsorted<T>>().mode()
54}
55
56pub fn modes<T, I>(it: I) -> (Vec<T>, usize, u32)
74where
75 T: PartialOrd + Clone + Send,
76 I: Iterator<Item = T>,
77{
78 it.collect::<Unsorted<T>>().modes()
79}
80
81pub fn antimodes<T, I>(it: I) -> (Vec<T>, usize, u32)
104where
105 T: PartialOrd + Clone + Send,
106 I: Iterator<Item = T>,
107{
108 let (antimodes_result, antimodes_count, antimodes_occurrences) =
109 it.collect::<Unsorted<T>>().antimodes();
110 (antimodes_result, antimodes_count, antimodes_occurrences)
111}
112
113pub fn gini<I>(it: I, precalc_sum: Option<f64>) -> Option<f64>
120where
121 I: Iterator,
122 <I as Iterator>::Item: PartialOrd + ToPrimitive + Send + Sync,
123{
124 it.collect::<Unsorted<_>>().gini(precalc_sum)
125}
126
127pub fn kurtosis<I>(it: I, precalc_mean: Option<f64>, precalc_variance: Option<f64>) -> Option<f64>
135where
136 I: Iterator,
137 <I as Iterator>::Item: PartialOrd + ToPrimitive + Send + Sync,
138{
139 it.collect::<Unsorted<_>>()
140 .kurtosis(precalc_mean, precalc_variance)
141}
142
143pub fn percentile_rank<I, V>(it: I, value: V) -> Option<f64>
150where
151 I: Iterator,
152 <I as Iterator>::Item: PartialOrd + ToPrimitive + Send + Sync,
153 V: PartialOrd + ToPrimitive,
154{
155 it.collect::<Unsorted<_>>().percentile_rank(value)
156}
157
158pub fn atkinson<I>(
166 it: I,
167 epsilon: f64,
168 precalc_mean: Option<f64>,
169 precalc_geometric_sum: Option<f64>,
170) -> Option<f64>
171where
172 I: Iterator,
173 <I as Iterator>::Item: PartialOrd + ToPrimitive + Send + Sync,
174{
175 it.collect::<Unsorted<_>>()
176 .atkinson(epsilon, precalc_mean, precalc_geometric_sum)
177}
178
179fn median_on_sorted<T>(data: &[T]) -> Option<f64>
180where
181 T: PartialOrd + ToPrimitive,
182{
183 Some(match data.len() {
184 0 => return None,
186 1 => data.first()?.to_f64()?,
188 len if len.is_multiple_of(2) => {
190 let idx = len / 2;
191 let v1 = unsafe { data.get_unchecked(idx - 1) }.to_f64()?;
194 let v2 = unsafe { data.get_unchecked(idx) }.to_f64()?;
195 f64::midpoint(v1, v2)
196 }
197 len => unsafe { data.get_unchecked(len / 2) }.to_f64()?,
200 })
201}
202
203fn mad_on_sorted<T>(data: &[T], precalc_median: Option<f64>) -> Option<f64>
204where
205 T: Sync + PartialOrd + ToPrimitive,
206{
207 if data.is_empty() {
208 return None;
209 }
210 let median_obs = precalc_median.unwrap_or_else(|| median_on_sorted(data).unwrap());
211
212 let mut abs_diff_vec: Vec<f64> = if data.len() < PARALLEL_THRESHOLD {
214 data.iter()
217 .map(|x| (median_obs - unsafe { x.to_f64().unwrap_unchecked() }).abs())
219 .collect()
220 } else {
221 data.par_iter()
223 .map(|x| (median_obs - unsafe { x.to_f64().unwrap_unchecked() }).abs())
225 .collect()
226 };
227
228 let len = abs_diff_vec.len();
230 let mid = len / 2;
231 let cmp = |a: &f64, b: &f64| a.total_cmp(b);
232
233 if len.is_multiple_of(2) {
234 abs_diff_vec.select_nth_unstable_by(mid, cmp);
236 let right = abs_diff_vec[mid];
237 let left = abs_diff_vec[..mid]
240 .iter()
241 .max_by(|a, b| cmp(a, b))
242 .copied()?;
243 Some(f64::midpoint(left, right))
244 } else {
245 abs_diff_vec.select_nth_unstable_by(mid, cmp);
246 Some(abs_diff_vec[mid])
247 }
248}
249
250fn gini_on_sorted<T>(data: &[Partial<T>], precalc_sum: Option<f64>) -> Option<f64>
251where
252 T: Sync + PartialOrd + ToPrimitive,
253{
254 let len = data.len();
255
256 if len == 0 {
258 return None;
259 }
260
261 if len == 1 {
263 return Some(0.0);
264 }
265
266 let first_val = unsafe { data.get_unchecked(0).0.to_f64().unwrap_unchecked() };
270 if first_val < 0.0 {
271 return None;
272 }
273
274 let sum = if let Some(precalc) = precalc_sum {
276 if precalc < 0.0 {
277 return None;
278 }
279 precalc
280 } else if len < PARALLEL_THRESHOLD {
281 data.iter()
283 .map(|x| unsafe { x.0.to_f64().unwrap_unchecked() })
285 .sum()
286 } else {
287 data.par_iter()
288 .map(|x| unsafe { x.0.to_f64().unwrap_unchecked() })
290 .sum()
291 };
292
293 let weighted_sum = if len < PARALLEL_THRESHOLD {
295 let mut weighted_sum = 0.0;
296 for (i, x) in data.iter().enumerate() {
297 let val = unsafe { x.0.to_f64().unwrap_unchecked() };
299 weighted_sum = ((i + 1) as f64).mul_add(val, weighted_sum);
300 }
301 weighted_sum
302 } else {
303 data.par_iter()
305 .enumerate()
306 .map(|(i, x)| {
307 let val = unsafe { x.0.to_f64().unwrap_unchecked() };
309 ((i + 1) as f64).mul_add(val, 0.0)
310 })
311 .sum()
312 };
313
314 if sum == 0.0 {
316 return None;
317 }
318
319 let n = len as f64;
323 let gini = 2.0f64.mul_add(weighted_sum / (n * sum), -(n + 1.0) / n);
324
325 Some(gini)
326}
327
328fn kurtosis_on_sorted<T>(
329 data: &[Partial<T>],
330 precalc_mean: Option<f64>,
331 precalc_variance: Option<f64>,
332) -> Option<f64>
333where
334 T: Sync + PartialOrd + ToPrimitive,
335{
336 let len = data.len();
337
338 if len < 4 {
340 return None;
341 }
342
343 let mean = if let Some(precalc) = precalc_mean {
345 precalc
346 } else {
347 let sum: f64 = if len < PARALLEL_THRESHOLD {
348 data.iter()
350 .map(|x| unsafe { x.0.to_f64().unwrap_unchecked() })
352 .sum()
353 } else {
354 data.par_iter()
355 .map(|x| unsafe { x.0.to_f64().unwrap_unchecked() })
357 .sum()
358 };
359 sum / len as f64
360 };
361
362 let (variance_sq, fourth_power_sum) = if let Some(variance) = precalc_variance {
366 if variance < 0.0 {
368 return None;
369 }
370 let variance_sq = variance * variance;
372
373 let fourth_power_sum = if len < PARALLEL_THRESHOLD {
375 let mut sum = 0.0;
376 for x in data {
377 let val = unsafe { x.0.to_f64().unwrap_unchecked() };
379 let diff = val - mean;
380 let diff_sq = diff * diff;
381 sum = diff_sq.mul_add(diff_sq, sum);
382 }
383 sum
384 } else {
385 data.par_iter()
386 .map(|x| {
387 let val = unsafe { x.0.to_f64().unwrap_unchecked() };
389 let diff = val - mean;
390 let diff_sq = diff * diff;
391 diff_sq * diff_sq
392 })
393 .sum()
394 };
395
396 (variance_sq, fourth_power_sum)
397 } else {
398 let (variance_sum, fourth_power_sum) = if len < PARALLEL_THRESHOLD {
400 let mut variance_sum = 0.0;
401 let mut fourth_power_sum = 0.0;
402
403 for x in data {
404 let val = unsafe { x.0.to_f64().unwrap_unchecked() };
406 let diff = val - mean;
407 let diff_sq = diff * diff;
408 variance_sum += diff_sq;
409 fourth_power_sum = diff_sq.mul_add(diff_sq, fourth_power_sum);
410 }
411
412 (variance_sum, fourth_power_sum)
413 } else {
414 data.par_iter()
416 .fold(
417 || (0.0_f64, 0.0_f64),
418 |acc, x| {
419 let val = unsafe { x.0.to_f64().unwrap_unchecked() };
421 let diff = val - mean;
422 let diff_sq = diff * diff;
423 (acc.0 + diff_sq, diff_sq.mul_add(diff_sq, acc.1))
424 },
425 )
426 .reduce(|| (0.0, 0.0), |a, b| (a.0 + b.0, a.1 + b.1))
427 };
428
429 let variance = variance_sum / len as f64;
430
431 if variance == 0.0 {
433 return None;
434 }
435
436 let variance_sq = variance * variance;
437 (variance_sq, fourth_power_sum)
438 };
439
440 if variance_sq == 0.0 {
442 return None;
443 }
444
445 let n = len as f64;
446
447 let denominator = (n - 1.0) * (n - 2.0) * (n - 3.0);
450 let adjustment = 3.0 * (n - 1.0) * (n - 1.0) / denominator;
451 let kurtosis = (n * (n + 1.0) * fourth_power_sum).mul_add(1.0 / (denominator * variance_sq), -adjustment);
452
453 Some(kurtosis)
454}
455
456fn percentile_rank_on_sorted<T, V>(data: &[Partial<T>], value: &V) -> Option<f64>
457where
458 T: PartialOrd + ToPrimitive,
459 V: PartialOrd + ToPrimitive,
460{
461 let len = data.len();
462
463 if len == 0 {
464 return None;
465 }
466
467 let value_f64 = value.to_f64()?;
468
469 let count_leq = data.binary_search_by(|x| {
472 x.0.to_f64()
473 .unwrap_or(f64::NAN)
474 .partial_cmp(&value_f64)
475 .unwrap_or(std::cmp::Ordering::Less)
476 });
477
478 let count = match count_leq {
479 Ok(idx) => {
480 let mut count = idx + 1;
482 for x in data.iter().skip(idx + 1) {
484 if let Some(x_val) = x.0.to_f64() {
485 if x_val.total_cmp(&value_f64).is_eq() {
486 count += 1;
487 } else {
488 break;
489 }
490 } else {
491 break;
492 }
493 }
494 count
495 }
496 Err(idx) => idx, };
498
499 Some((count as f64 / len as f64) * 100.0)
501}
502
503fn atkinson_on_sorted<T>(
504 data: &[Partial<T>],
505 epsilon: f64,
506 precalc_mean: Option<f64>,
507 precalc_geometric_sum: Option<f64>,
508) -> Option<f64>
509where
510 T: Sync + PartialOrd + ToPrimitive,
511{
512 let len = data.len();
513
514 if len == 0 {
516 return None;
517 }
518
519 if len == 1 {
521 return Some(0.0);
522 }
523
524 if epsilon < 0.0 {
526 return None;
527 }
528
529 let mean = if let Some(precalc) = precalc_mean {
531 precalc
532 } else {
533 let sum: f64 = if len < PARALLEL_THRESHOLD {
534 data.iter()
536 .map(|x| unsafe { x.0.to_f64().unwrap_unchecked() })
538 .sum()
539 } else {
540 data.par_iter()
541 .map(|x| unsafe { x.0.to_f64().unwrap_unchecked() })
543 .sum()
544 };
545 sum / len as f64
546 };
547
548 if mean == 0.0 {
550 return None;
551 }
552
553 if (epsilon - 1.0).abs() < 1e-10 {
555 let geometric_sum: f64 = if let Some(precalc) = precalc_geometric_sum {
557 precalc
558 } else if len < PARALLEL_THRESHOLD {
559 let mut sum = 0.0;
560 for x in data {
561 let val = unsafe { x.0.to_f64().unwrap_unchecked() };
563 if val <= 0.0 {
564 return None;
566 }
567 sum += val.ln();
568 }
569 sum
570 } else {
571 data.par_iter()
572 .map(|x| {
573 let val = unsafe { x.0.to_f64().unwrap_unchecked() };
575 if val <= 0.0 {
576 return f64::NAN;
577 }
578 val.ln()
579 })
580 .sum()
581 };
582
583 if geometric_sum.is_nan() {
584 return None;
585 }
586
587 let geometric_mean = (geometric_sum / len as f64).exp();
588 return Some(1.0 - geometric_mean / mean);
589 }
590
591 let exponent = 1.0 - epsilon;
594
595 let sum_powered: f64 = if len < PARALLEL_THRESHOLD {
596 let mut sum = 0.0;
597 for x in data {
598 let val = unsafe { x.0.to_f64().unwrap_unchecked() };
600 if val < 0.0 {
601 return None;
603 }
604 let ratio = val / mean;
605 sum += ratio.powf(exponent);
606 }
607 sum
608 } else {
609 data.par_iter()
610 .map(|x| {
611 let val = unsafe { x.0.to_f64().unwrap_unchecked() };
613 if val < 0.0 {
614 return f64::NAN;
615 }
616 let ratio = val / mean;
617 ratio.powf(exponent)
618 })
619 .sum()
620 };
621
622 if sum_powered.is_nan() || sum_powered <= 0.0 {
623 return None;
624 }
625
626 let atkinson = 1.0 - (sum_powered / len as f64).powf(1.0 / exponent);
627 Some(atkinson)
628}
629
630#[cfg(test)]
633fn quickselect<T>(data: &mut [Partial<T>], k: usize) -> Option<&T>
634where
635 T: PartialOrd,
636{
637 if data.is_empty() || k >= data.len() {
638 return None;
639 }
640
641 let mut left = 0;
642 let mut right = data.len() - 1;
643
644 loop {
645 if left == right {
646 return Some(&data[left].0);
647 }
648
649 let pivot_idx = median_of_three_pivot(data, left, right);
651 let pivot_idx = partition(data, left, right, pivot_idx);
652
653 match k.cmp(&pivot_idx) {
654 std::cmp::Ordering::Equal => return Some(&data[pivot_idx].0),
655 std::cmp::Ordering::Less => right = pivot_idx - 1,
656 std::cmp::Ordering::Greater => left = pivot_idx + 1,
657 }
658 }
659}
660
661#[cfg(test)]
663fn median_of_three_pivot<T>(data: &[Partial<T>], left: usize, right: usize) -> usize
664where
665 T: PartialOrd,
666{
667 let mid = left + (right - left) / 2;
668
669 if data[left] <= data[mid] {
670 if data[mid] <= data[right] {
671 mid
672 } else if data[left] <= data[right] {
673 right
674 } else {
675 left
676 }
677 } else if data[left] <= data[right] {
678 left
679 } else if data[mid] <= data[right] {
680 right
681 } else {
682 mid
683 }
684}
685
686#[cfg(test)]
688fn partition<T>(data: &mut [Partial<T>], left: usize, right: usize, pivot_idx: usize) -> usize
689where
690 T: PartialOrd,
691{
692 data.swap(pivot_idx, right);
694 let mut store_idx = left;
695
696 for i in left..right {
699 if unsafe { data.get_unchecked(i) <= data.get_unchecked(right) } {
702 data.swap(i, store_idx);
703 store_idx += 1;
704 }
705 }
706
707 data.swap(store_idx, right);
709 store_idx
710}
711
712fn quartiles_on_sorted<T>(data: &[Partial<T>]) -> Option<(f64, f64, f64)>
716where
717 T: PartialOrd + ToPrimitive,
718{
719 let len = data.len();
720
721 match len {
723 0..=2 => return None,
724 3 => {
725 return Some(
726 unsafe {
728 (
729 data.get_unchecked(0).0.to_f64()?,
730 data.get_unchecked(1).0.to_f64()?,
731 data.get_unchecked(2).0.to_f64()?,
732 )
733 },
734 );
735 }
736 _ => {}
737 }
738
739 let k = len / 4;
741 let remainder = len % 4;
742
743 unsafe {
746 Some(match remainder {
747 0 => {
748 let q1 = f64::midpoint(
759 data.get_unchecked(k - 1).0.to_f64()?,
760 data.get_unchecked(k).0.to_f64()?,
761 );
762 let q2 = f64::midpoint(
763 data.get_unchecked(2 * k - 1).0.to_f64()?,
764 data.get_unchecked(2 * k).0.to_f64()?,
765 );
766 let q3 = f64::midpoint(
767 data.get_unchecked(3 * k - 1).0.to_f64()?,
768 data.get_unchecked(3 * k).0.to_f64()?,
769 );
770 (q1, q2, q3)
771 }
772 1 => {
773 let q1 = f64::midpoint(
784 data.get_unchecked(k - 1).0.to_f64()?,
785 data.get_unchecked(k).0.to_f64()?,
786 );
787 let q2 = data.get_unchecked(2 * k).0.to_f64()?;
788 let q3 = f64::midpoint(
789 data.get_unchecked(3 * k).0.to_f64()?,
790 data.get_unchecked(3 * k + 1).0.to_f64()?,
791 );
792 (q1, q2, q3)
793 }
794 2 => {
795 let q1 = data.get_unchecked(k).0.to_f64()?;
806 let q2 = f64::midpoint(
807 data.get_unchecked(2 * k).0.to_f64()?,
808 data.get_unchecked(2 * k + 1).0.to_f64()?,
809 );
810 let q3 = data.get_unchecked(3 * k + 1).0.to_f64()?;
811 (q1, q2, q3)
812 }
813 _ => {
814 let q1 = data.get_unchecked(k).0.to_f64()?;
825 let q2 = data.get_unchecked(2 * k + 1).0.to_f64()?;
826 let q3 = data.get_unchecked(3 * k + 2).0.to_f64()?;
827 (q1, q2, q3)
828 }
829 })
830 }
831}
832
833fn quartiles_with_zero_copy_selection<T>(data: &[Partial<T>]) -> Option<(f64, f64, f64)>
842where
843 T: PartialOrd + ToPrimitive,
844{
845 let len = data.len();
846
847 match len {
849 0..=2 => return None,
850 3 => {
851 let mut indices: Vec<usize> = (0..3).collect();
852 let cmp =
853 |a: &usize, b: &usize| data[*a].partial_cmp(&data[*b]).unwrap_or(std::cmp::Ordering::Less);
854 indices.sort_unstable_by(cmp);
855 let min_val = data[indices[0]].0.to_f64()?;
856 let med_val = data[indices[1]].0.to_f64()?;
857 let max_val = data[indices[2]].0.to_f64()?;
858 return Some((min_val, med_val, max_val));
859 }
860 _ => {}
861 }
862
863 let k = len / 4;
864 let remainder = len % 4;
865
866 let mut indices: Vec<usize> = (0..len).collect();
867 let cmp = |a: &usize, b: &usize| {
868 data[*a]
869 .partial_cmp(&data[*b])
870 .unwrap_or(std::cmp::Ordering::Less)
871 };
872
873 let raw_positions: Vec<usize> = match remainder {
878 0 => vec![k - 1, k, 2 * k - 1, 2 * k, 3 * k - 1, 3 * k],
879 1 => vec![k - 1, k, 2 * k, 3 * k, 3 * k + 1],
880 2 => vec![k, 2 * k, 2 * k + 1, 3 * k + 1],
881 _ => vec![k, 2 * k + 1, 3 * k + 2],
882 };
883
884 let mut unique_positions = raw_positions.clone();
885 unique_positions.dedup();
886
887 let mut start = 0;
889 for &pos in &unique_positions {
890 indices[start..].select_nth_unstable_by(pos - start, &cmp);
891 start = pos + 1;
892 }
893
894 let values: Vec<f64> = raw_positions
896 .iter()
897 .map(|&pos| data[indices[pos]].0.to_f64())
898 .collect::<Option<Vec<_>>>()?;
899
900 match remainder {
901 0 => {
902 let q1 = f64::midpoint(values[0], values[1]);
903 let q2 = f64::midpoint(values[2], values[3]);
904 let q3 = f64::midpoint(values[4], values[5]);
905 Some((q1, q2, q3))
906 }
907 1 => {
908 let q1 = f64::midpoint(values[0], values[1]);
909 let q2 = values[2];
910 let q3 = f64::midpoint(values[3], values[4]);
911 Some((q1, q2, q3))
912 }
913 2 => {
914 let q1 = values[0];
915 let q2 = f64::midpoint(values[1], values[2]);
916 let q3 = values[3];
917 Some((q1, q2, q3))
918 }
919 _ => Some((values[0], values[1], values[2])),
920 }
921}
922
923fn mode_on_sorted<T, I>(it: I) -> Option<T>
924where
925 T: PartialOrd,
926 I: Iterator<Item = T>,
927{
928 use std::cmp::Ordering;
929
930 let (mut mode, mut next) = (None, None);
937 let (mut mode_count, mut next_count) = (0usize, 0usize);
938 for x in it {
939 if mode.as_ref() == Some(&x) {
940 mode_count += 1;
941 } else if next.as_ref() == Some(&x) {
942 next_count += 1;
943 } else {
944 next = Some(x);
945 next_count = 0;
946 }
947
948 match next_count.cmp(&mode_count) {
949 Ordering::Greater => {
950 mode = next;
951 mode_count = next_count;
952 next = None;
953 next_count = 0;
954 }
955 Ordering::Equal => {
956 mode = None;
957 mode_count = 0;
958 }
959 Ordering::Less => {}
960 }
961 }
962 mode
963}
964
965#[allow(clippy::type_complexity)]
993#[inline]
994fn modes_and_antimodes_on_sorted_slice<T>(
995 data: &[Partial<T>],
996) -> ((Vec<T>, usize, u32), (Vec<T>, usize, u32))
997where
998 T: PartialOrd + Clone,
999{
1000 let size = data.len();
1001
1002 if size == 0 {
1004 return ((Vec::new(), 0, 0), (Vec::new(), 0, 0));
1005 }
1006
1007 let mut x = size;
1010 let mut y = x.div_ceil(2);
1011 while y < x {
1012 x = y;
1013 y = usize::midpoint(x, size / x);
1014 }
1015 let sqrt_size = x;
1016 let mut runs: Vec<(&T, u32)> = Vec::with_capacity(sqrt_size.clamp(16, 1_000));
1017
1018 let mut current_value = &data[0].0;
1019 let mut current_count = 1;
1020 let mut highest_count = 1;
1021 let mut lowest_count = u32::MAX;
1022
1023 for x in data.iter().skip(1) {
1025 if x.0 == *current_value {
1026 current_count += 1;
1027 highest_count = highest_count.max(current_count);
1028 } else {
1029 runs.push((current_value, current_count));
1030 lowest_count = lowest_count.min(current_count);
1031 current_value = &x.0;
1032 current_count = 1;
1033 }
1034 }
1035 runs.push((current_value, current_count));
1036 lowest_count = lowest_count.min(current_count);
1037
1038 if runs.len() == 1 {
1040 let (val, count) = runs.pop().unwrap();
1041 return ((vec![val.clone()], 1, count), (Vec::new(), 0, 0));
1042 }
1043
1044 if highest_count == 1 {
1046 let antimodes_count = runs.len().min(10);
1047 let total_count = runs.len();
1048 let mut antimodes = Vec::with_capacity(antimodes_count);
1049 for (val, _) in runs.into_iter().take(antimodes_count) {
1050 antimodes.push(val.clone());
1051 }
1052 return ((Vec::new(), 0, 0), (antimodes, total_count, 1));
1054 }
1055
1056 let estimated_modes = (runs.len() / 10).clamp(1, 10);
1059 let estimated_antimodes = 10.min(runs.len());
1060
1061 let mut modes_result = Vec::with_capacity(estimated_modes);
1062 let mut antimodes_result = Vec::with_capacity(estimated_antimodes);
1063 let mut mode_count = 0;
1064 let mut antimodes_count = 0;
1065 let mut antimodes_collected = 0_u32;
1066
1067 for (val, count) in &runs {
1068 if *count == highest_count {
1069 modes_result.push((*val).clone());
1070 mode_count += 1;
1071 }
1072 if *count == lowest_count {
1073 antimodes_count += 1;
1074 if antimodes_collected < 10 {
1075 antimodes_result.push((*val).clone());
1076 antimodes_collected += 1;
1077 }
1078 }
1079 }
1080
1081 (
1082 (modes_result, mode_count, highest_count),
1083 (antimodes_result, antimodes_count, lowest_count),
1084 )
1085}
1086
1087#[allow(clippy::unsafe_derive_deserialize)]
1095#[derive(Clone, Serialize, Deserialize, Eq, PartialEq)]
1096pub struct Unsorted<T> {
1097 sorted: bool,
1098 data: Vec<Partial<T>>,
1099}
1100
1101impl<T: PartialOrd + Send> Unsorted<T> {
1102 #[inline]
1104 #[must_use]
1105 pub fn new() -> Unsorted<T> {
1106 Default::default()
1107 }
1108
1109 #[allow(clippy::inline_always)]
1111 #[inline(always)]
1112 pub fn add(&mut self, v: T) {
1113 self.sorted = false;
1114 self.data.push(Partial(v));
1115 }
1116
1117 #[inline]
1119 #[must_use]
1120 pub const fn len(&self) -> usize {
1121 self.data.len()
1122 }
1123
1124 #[inline]
1125 #[must_use]
1126 pub const fn is_empty(&self) -> bool {
1127 self.data.is_empty()
1128 }
1129
1130 #[inline]
1131 fn sort(&mut self) {
1132 if !self.sorted {
1133 if self.data.len() < PARALLEL_THRESHOLD {
1135 self.data.sort_unstable();
1136 } else {
1137 self.data.par_sort_unstable();
1138 }
1139 self.sorted = true;
1140 }
1141 }
1142
1143 #[inline]
1144 const fn already_sorted(&mut self) {
1145 self.sorted = true;
1146 }
1147
1148 #[inline]
1150 pub fn add_bulk(&mut self, values: Vec<T>) {
1151 self.sorted = false;
1152 self.data.reserve(values.len());
1153 self.data.extend(values.into_iter().map(Partial));
1154 }
1155
1156 #[inline]
1158 pub fn shrink_to_fit(&mut self) {
1159 self.data.shrink_to_fit();
1160 }
1161
1162 #[inline]
1164 #[must_use]
1165 pub fn with_capacity(capacity: usize) -> Self {
1166 Unsorted {
1167 sorted: true,
1168 data: Vec::with_capacity(capacity),
1169 }
1170 }
1171
1172 #[inline]
1174 pub fn push_ascending(&mut self, value: T) {
1175 if let Some(last) = self.data.last() {
1176 debug_assert!(last.0 <= value, "Value must be >= than last element");
1177 }
1178 self.data.push(Partial(value));
1179 }
1181}
1182
1183impl<T: PartialOrd + PartialEq + Clone + Send + Sync> Unsorted<T> {
1184 #[inline]
1185 pub fn cardinality(&mut self, sorted: bool, parallel_threshold: usize) -> u64 {
1193 const CHUNK_SIZE: usize = 2048; const DEFAULT_PARALLEL_THRESHOLD: usize = 10_240; let len = self.data.len();
1197 match len {
1198 0 => return 0,
1199 1 => return 1,
1200 _ => {}
1201 }
1202
1203 if sorted {
1204 self.already_sorted();
1205 } else {
1206 self.sort();
1207 }
1208
1209 let use_parallel = parallel_threshold != 0
1210 && (parallel_threshold == 1
1211 || len > parallel_threshold.max(DEFAULT_PARALLEL_THRESHOLD));
1212
1213 if use_parallel {
1214 type ChunkInfo<'a, T> = Vec<(u64, Option<&'a Partial<T>>, Option<&'a Partial<T>>)>;
1217 let chunk_info: ChunkInfo<'_, T> = self
1218 .data
1219 .par_chunks(CHUNK_SIZE)
1220 .map(|chunk| {
1221 let mut count = u64::from(!chunk.is_empty());
1223 for window in chunk.windows(2) {
1224 if unsafe { window.get_unchecked(0) != window.get_unchecked(1) } {
1226 count += 1;
1227 }
1228 }
1229 (count, chunk.first(), chunk.last())
1230 })
1231 .collect();
1232
1233 let mut total = 0;
1235 for (i, &(count, first_opt, _last_opt)) in chunk_info.iter().enumerate() {
1236 total += count;
1237
1238 if i > 0
1240 && let (Some(prev_last), Some(curr_first)) = (chunk_info[i - 1].2, first_opt)
1241 && prev_last == curr_first
1242 {
1243 total -= 1; }
1245 }
1246
1247 total
1248 } else {
1249 let mut count = u64::from(!self.data.is_empty());
1254
1255 for window in self.data.windows(2) {
1256 if unsafe { window.get_unchecked(0) != window.get_unchecked(1) } {
1258 count += 1;
1259 }
1260 }
1261 count
1262 }
1263 }
1264}
1265
1266impl<T: PartialOrd + Clone + Send> Unsorted<T> {
1267 #[inline]
1269 pub fn mode(&mut self) -> Option<T> {
1270 if self.data.is_empty() {
1271 return None;
1272 }
1273 self.sort();
1274 mode_on_sorted(self.data.iter().map(|p| &p.0)).cloned()
1275 }
1276
1277 #[inline]
1281 fn modes(&mut self) -> (Vec<T>, usize, u32) {
1282 if self.data.is_empty() {
1283 return (Vec::new(), 0, 0);
1284 }
1285 self.sort();
1286 modes_and_antimodes_on_sorted_slice(&self.data).0
1287 }
1288
1289 #[inline]
1292 fn antimodes(&mut self) -> (Vec<T>, usize, u32) {
1293 if self.data.is_empty() {
1294 return (Vec::new(), 0, 0);
1295 }
1296 self.sort();
1297 modes_and_antimodes_on_sorted_slice(&self.data).1
1298 }
1299
1300 #[allow(clippy::type_complexity)]
1303 #[inline]
1304 pub fn modes_antimodes(&mut self) -> ((Vec<T>, usize, u32), (Vec<T>, usize, u32)) {
1305 if self.data.is_empty() {
1306 return ((Vec::new(), 0, 0), (Vec::new(), 0, 0));
1307 }
1308 self.sort();
1309 modes_and_antimodes_on_sorted_slice(&self.data)
1310 }
1311}
1312
1313impl Unsorted<Vec<u8>> {
1314 #[allow(clippy::inline_always)]
1321 #[inline(always)]
1322 pub fn add_bytes(&mut self, v: &[u8]) {
1323 self.sorted = false;
1324 self.data.push(Partial(v.to_vec()));
1325 }
1326}
1327
1328impl<T: PartialOrd + ToPrimitive + Send> Unsorted<T> {
1329 #[inline]
1331 pub fn median(&mut self) -> Option<f64> {
1332 if self.data.is_empty() {
1333 return None;
1334 }
1335 self.sort();
1336 median_on_sorted(&self.data)
1337 }
1338}
1339
1340impl<T: PartialOrd + ToPrimitive + Send + Sync> Unsorted<T> {
1341 #[inline]
1343 pub fn mad(&mut self, existing_median: Option<f64>) -> Option<f64> {
1344 if self.data.is_empty() {
1345 return None;
1346 }
1347 if existing_median.is_none() {
1348 self.sort();
1349 }
1350 mad_on_sorted(&self.data, existing_median)
1351 }
1352}
1353
1354impl<T: PartialOrd + ToPrimitive + Send> Unsorted<T> {
1355 #[inline]
1360 pub fn quartiles(&mut self) -> Option<(f64, f64, f64)> {
1361 if self.data.is_empty() {
1362 return None;
1363 }
1364 self.sort();
1365 quartiles_on_sorted(&self.data)
1366 }
1367}
1368
1369impl<T: PartialOrd + ToPrimitive + Send + Sync> Unsorted<T> {
1370 #[inline]
1376 pub fn gini(&mut self, precalc_sum: Option<f64>) -> Option<f64> {
1377 if self.data.is_empty() {
1378 return None;
1379 }
1380 self.sort();
1381 gini_on_sorted(&self.data, precalc_sum)
1382 }
1383
1384 #[inline]
1391 pub fn kurtosis(
1392 &mut self,
1393 precalc_mean: Option<f64>,
1394 precalc_variance: Option<f64>,
1395 ) -> Option<f64> {
1396 if self.data.is_empty() {
1397 return None;
1398 }
1399 self.sort();
1400 kurtosis_on_sorted(&self.data, precalc_mean, precalc_variance)
1401 }
1402
1403 #[inline]
1410 pub fn percentile_rank<V>(&mut self, value: V) -> Option<f64>
1411 where
1412 V: PartialOrd + ToPrimitive,
1413 {
1414 if self.data.is_empty() {
1415 return None;
1416 }
1417 self.sort();
1418 percentile_rank_on_sorted(&self.data, &value)
1419 }
1420
1421 #[inline]
1438 pub fn atkinson(
1439 &mut self,
1440 epsilon: f64,
1441 precalc_mean: Option<f64>,
1442 precalc_geometric_sum: Option<f64>,
1443 ) -> Option<f64> {
1444 if self.data.is_empty() {
1445 return None;
1446 }
1447 self.sort();
1448 atkinson_on_sorted(&self.data, epsilon, precalc_mean, precalc_geometric_sum)
1449 }
1450}
1451
1452impl<T: PartialOrd + ToPrimitive + Clone + Send> Unsorted<T> {
1453 #[inline]
1465 pub fn quartiles_with_selection(&mut self) -> Option<(f64, f64, f64)> {
1466 if self.data.is_empty() {
1467 return None;
1468 }
1469 quartiles_with_zero_copy_selection(&self.data)
1471 }
1472}
1473
1474impl<T: PartialOrd + ToPrimitive + Send> Unsorted<T> {
1475 #[inline]
1481 #[must_use]
1482 pub fn quartiles_zero_copy(&self) -> Option<(f64, f64, f64)> {
1483 if self.data.is_empty() {
1484 return None;
1485 }
1486 quartiles_with_zero_copy_selection(&self.data)
1487 }
1488}
1489
1490impl<T: PartialOrd + Send> Commute for Unsorted<T> {
1491 #[inline]
1492 fn merge(&mut self, mut v: Unsorted<T>) {
1493 if v.is_empty() {
1494 return;
1495 }
1496
1497 self.sorted = false;
1498 self.data.extend(std::mem::take(&mut v.data));
1500 }
1501}
1502
1503impl<T: PartialOrd> Default for Unsorted<T> {
1504 #[inline]
1505 fn default() -> Unsorted<T> {
1506 Unsorted {
1507 data: Vec::with_capacity(16),
1508 sorted: true, }
1510 }
1511}
1512
1513impl<T: PartialOrd + Send> FromIterator<T> for Unsorted<T> {
1514 #[inline]
1515 fn from_iter<I: IntoIterator<Item = T>>(it: I) -> Unsorted<T> {
1516 let mut v = Unsorted::new();
1517 v.extend(it);
1518 v
1519 }
1520}
1521
1522impl<T: PartialOrd> Extend<T> for Unsorted<T> {
1523 #[inline]
1524 fn extend<I: IntoIterator<Item = T>>(&mut self, it: I) {
1525 self.sorted = false;
1526 self.data.extend(it.into_iter().map(Partial));
1527 }
1528}
1529
1530fn custom_percentiles_on_sorted<T>(data: &[Partial<T>], percentiles: &[u8]) -> Option<Vec<T>>
1531where
1532 T: PartialOrd + Clone,
1533{
1534 let len = data.len();
1535
1536 if len == 0 || percentiles.iter().any(|&p| p > 100) {
1538 return None;
1539 }
1540
1541 let unique_percentiles: Vec<u8> = if percentiles.len() <= 1 {
1543 percentiles.to_vec()
1545 } else {
1546 let is_sorted_unique = percentiles.windows(2).all(|w| w[0] < w[1]);
1548
1549 if is_sorted_unique {
1550 percentiles.to_vec()
1552 } else {
1553 let mut seen = [false; 101];
1555 let mut sorted_unique = Vec::with_capacity(percentiles.len().min(101));
1556 for &p in percentiles {
1557 if !seen[p as usize] {
1558 seen[p as usize] = true;
1559 sorted_unique.push(p);
1560 }
1561 }
1562 sorted_unique.sort_unstable();
1563 sorted_unique
1564 }
1565 };
1566
1567 let mut results = Vec::with_capacity(unique_percentiles.len());
1568
1569 unsafe {
1573 for &p in &unique_percentiles {
1574 #[allow(clippy::cast_sign_loss)]
1578 let rank = ((f64::from(p) / 100.0) * len as f64).ceil() as usize;
1579
1580 let idx = rank.saturating_sub(1);
1582
1583 results.push(data.get_unchecked(idx).0.clone());
1585 }
1586 }
1587
1588 Some(results)
1589}
1590
1591impl<T: PartialOrd + Clone + Send> Unsorted<T> {
1592 #[inline]
1614 pub fn custom_percentiles(&mut self, percentiles: &[u8]) -> Option<Vec<T>> {
1615 if self.data.is_empty() {
1616 return None;
1617 }
1618 self.sort();
1619 custom_percentiles_on_sorted(&self.data, percentiles)
1620 }
1621}
1622
1623#[cfg(test)]
1624mod test {
1625 use super::*;
1626
1627 #[test]
1628 fn test_cardinality_empty() {
1629 let mut unsorted: Unsorted<i32> = Unsorted::new();
1630 assert_eq!(unsorted.cardinality(false, 1), 0);
1631 }
1632
1633 #[test]
1634 fn test_cardinality_single_element() {
1635 let mut unsorted = Unsorted::new();
1636 unsorted.add(5);
1637 assert_eq!(unsorted.cardinality(false, 1), 1);
1638 }
1639
1640 #[test]
1641 fn test_cardinality_unique_elements() {
1642 let mut unsorted = Unsorted::new();
1643 unsorted.extend(vec![1, 2, 3, 4, 5]);
1644 assert_eq!(unsorted.cardinality(false, 1), 5);
1645 }
1646
1647 #[test]
1648 fn test_cardinality_duplicate_elements() {
1649 let mut unsorted = Unsorted::new();
1650 unsorted.extend(vec![1, 2, 2, 3, 3, 3, 4, 4, 4, 4]);
1651 assert_eq!(unsorted.cardinality(false, 1), 4);
1652 }
1653
1654 #[test]
1655 fn test_cardinality_all_same() {
1656 let mut unsorted = Unsorted::new();
1657 unsorted.extend(vec![1; 100]);
1658 assert_eq!(unsorted.cardinality(false, 1), 1);
1659 }
1660
1661 #[test]
1662 fn test_cardinality_large_range() {
1663 let mut unsorted = Unsorted::new();
1664 unsorted.extend(0..1_000_000);
1665 assert_eq!(unsorted.cardinality(false, 1), 1_000_000);
1666 }
1667
1668 #[test]
1669 fn test_cardinality_large_range_sequential() {
1670 let mut unsorted = Unsorted::new();
1671 unsorted.extend(0..1_000_000);
1672 assert_eq!(unsorted.cardinality(false, 2_000_000), 1_000_000);
1673 }
1674
1675 #[test]
1676 fn test_cardinality_presorted() {
1677 let mut unsorted = Unsorted::new();
1678 unsorted.extend(vec![1, 2, 3, 4, 5]);
1679 unsorted.sort();
1680 assert_eq!(unsorted.cardinality(true, 1), 5);
1681 }
1682
1683 #[test]
1684 fn test_cardinality_float() {
1685 let mut unsorted = Unsorted::new();
1686 unsorted.extend(vec![1.0, 1.0, 2.0, 3.0, 3.0, 4.0]);
1687 assert_eq!(unsorted.cardinality(false, 1), 4);
1688 }
1689
1690 #[test]
1691 fn test_cardinality_string() {
1692 let mut unsorted = Unsorted::new();
1693 unsorted.extend(vec!["a", "b", "b", "c", "c", "c"]);
1694 assert_eq!(unsorted.cardinality(false, 1), 3);
1695 }
1696
1697 #[test]
1698 fn test_quartiles_selection_vs_sorted() {
1699 let test_cases = vec![
1701 vec![3, 5, 7, 9],
1702 vec![3, 5, 7],
1703 vec![1, 2, 7, 11],
1704 vec![3, 5, 7, 9, 12],
1705 vec![2, 2, 3, 8, 10],
1706 vec![3, 5, 7, 9, 12, 20],
1707 vec![0, 2, 4, 8, 10, 11],
1708 vec![3, 5, 7, 9, 12, 20, 21],
1709 vec![1, 5, 6, 6, 7, 10, 19],
1710 ];
1711
1712 for test_case in test_cases {
1713 let mut unsorted1 = Unsorted::new();
1714 let mut unsorted2 = Unsorted::new();
1715 let mut unsorted3 = Unsorted::new();
1716 unsorted1.extend(test_case.clone());
1717 unsorted2.extend(test_case.clone());
1718 unsorted3.extend(test_case.clone());
1719
1720 let result_sorted = unsorted1.quartiles();
1721 let result_selection = unsorted2.quartiles_with_selection();
1722 let result_zero_copy = unsorted3.quartiles_zero_copy();
1723
1724 assert_eq!(
1725 result_sorted, result_selection,
1726 "Selection mismatch for test case: {:?}",
1727 test_case
1728 );
1729 assert_eq!(
1730 result_sorted, result_zero_copy,
1731 "Zero-copy mismatch for test case: {:?}",
1732 test_case
1733 );
1734 }
1735 }
1736
1737 #[test]
1738 fn test_quartiles_with_selection_small() {
1739 let mut unsorted: Unsorted<i32> = Unsorted::new();
1741 assert_eq!(unsorted.quartiles_with_selection(), None);
1742
1743 let mut unsorted = Unsorted::new();
1744 unsorted.extend(vec![1, 2]);
1745 assert_eq!(unsorted.quartiles_with_selection(), None);
1746
1747 let mut unsorted = Unsorted::new();
1748 unsorted.extend(vec![1, 2, 3]);
1749 assert_eq!(unsorted.quartiles_with_selection(), Some((1.0, 2.0, 3.0)));
1750 }
1751
1752 #[test]
1753 fn test_quickselect() {
1754 let data = vec![
1755 Partial(3),
1756 Partial(1),
1757 Partial(4),
1758 Partial(1),
1759 Partial(5),
1760 Partial(9),
1761 Partial(2),
1762 Partial(6),
1763 ];
1764
1765 assert_eq!(quickselect(&mut data.clone(), 0), Some(&1));
1767 assert_eq!(quickselect(&mut data.clone(), 3), Some(&3));
1768 assert_eq!(quickselect(&mut data.clone(), 7), Some(&9));
1769
1770 let mut empty: Vec<Partial<i32>> = vec![];
1772 assert_eq!(quickselect(&mut empty, 0), None);
1773
1774 let mut data = vec![Partial(3), Partial(1), Partial(4), Partial(1), Partial(5)];
1775 assert_eq!(quickselect(&mut data, 10), None); }
1777
1778 #[test]
1779 fn median_stream() {
1780 assert_eq!(median(vec![3usize, 5, 7, 9].into_iter()), Some(6.0));
1781 assert_eq!(median(vec![3usize, 5, 7].into_iter()), Some(5.0));
1782 }
1783
1784 #[test]
1785 fn mad_stream() {
1786 assert_eq!(mad(vec![3usize, 5, 7, 9].into_iter(), None), Some(2.0));
1787 assert_eq!(
1788 mad(
1789 vec![
1790 86usize, 60, 95, 39, 49, 12, 56, 82, 92, 24, 33, 28, 46, 34, 100, 39, 100, 38,
1791 50, 61, 39, 88, 5, 13, 64
1792 ]
1793 .into_iter(),
1794 None
1795 ),
1796 Some(16.0)
1797 );
1798 }
1799
1800 #[test]
1801 fn mad_stream_precalc_median() {
1802 let data = vec![3usize, 5, 7, 9].into_iter();
1803 let median1 = median(data.clone());
1804 assert_eq!(mad(data, median1), Some(2.0));
1805
1806 let data2 = vec![
1807 86usize, 60, 95, 39, 49, 12, 56, 82, 92, 24, 33, 28, 46, 34, 100, 39, 100, 38, 50, 61,
1808 39, 88, 5, 13, 64,
1809 ]
1810 .into_iter();
1811 let median2 = median(data2.clone());
1812 assert_eq!(mad(data2, median2), Some(16.0));
1813 }
1814
1815 #[test]
1816 fn mode_stream() {
1817 assert_eq!(mode(vec![3usize, 5, 7, 9].into_iter()), None);
1818 assert_eq!(mode(vec![3usize, 3, 3, 3].into_iter()), Some(3));
1819 assert_eq!(mode(vec![3usize, 3, 3, 4].into_iter()), Some(3));
1820 assert_eq!(mode(vec![4usize, 3, 3, 3].into_iter()), Some(3));
1821 assert_eq!(mode(vec![1usize, 1, 2, 3, 3].into_iter()), None);
1822 }
1823
1824 #[test]
1825 fn median_floats() {
1826 assert_eq!(median(vec![3.0f64, 5.0, 7.0, 9.0].into_iter()), Some(6.0));
1827 assert_eq!(median(vec![3.0f64, 5.0, 7.0].into_iter()), Some(5.0));
1828 }
1829
1830 #[test]
1831 fn mode_floats() {
1832 assert_eq!(mode(vec![3.0f64, 5.0, 7.0, 9.0].into_iter()), None);
1833 assert_eq!(mode(vec![3.0f64, 3.0, 3.0, 3.0].into_iter()), Some(3.0));
1834 assert_eq!(mode(vec![3.0f64, 3.0, 3.0, 4.0].into_iter()), Some(3.0));
1835 assert_eq!(mode(vec![4.0f64, 3.0, 3.0, 3.0].into_iter()), Some(3.0));
1836 assert_eq!(mode(vec![1.0f64, 1.0, 2.0, 3.0, 3.0].into_iter()), None);
1837 }
1838
1839 #[test]
1840 fn modes_stream() {
1841 assert_eq!(modes(vec![3usize, 5, 7, 9].into_iter()), (vec![], 0, 0));
1842 assert_eq!(modes(vec![3usize, 3, 3, 3].into_iter()), (vec![3], 1, 4));
1843 assert_eq!(modes(vec![3usize, 3, 4, 4].into_iter()), (vec![3, 4], 2, 2));
1844 assert_eq!(modes(vec![4usize, 3, 3, 3].into_iter()), (vec![3], 1, 3));
1845 assert_eq!(modes(vec![1usize, 1, 2, 2].into_iter()), (vec![1, 2], 2, 2));
1846 let vec: Vec<u32> = vec![];
1847 assert_eq!(modes(vec.into_iter()), (vec![], 0, 0));
1848 }
1849
1850 #[test]
1851 fn modes_floats() {
1852 assert_eq!(
1853 modes(vec![3_f64, 5.0, 7.0, 9.0].into_iter()),
1854 (vec![], 0, 0)
1855 );
1856 assert_eq!(
1857 modes(vec![3_f64, 3.0, 3.0, 3.0].into_iter()),
1858 (vec![3.0], 1, 4)
1859 );
1860 assert_eq!(
1861 modes(vec![3_f64, 3.0, 4.0, 4.0].into_iter()),
1862 (vec![3.0, 4.0], 2, 2)
1863 );
1864 assert_eq!(
1865 modes(vec![1_f64, 1.0, 2.0, 3.0, 3.0].into_iter()),
1866 (vec![1.0, 3.0], 2, 2)
1867 );
1868 }
1869
1870 #[test]
1871 fn antimodes_stream() {
1872 assert_eq!(
1873 antimodes(vec![3usize, 5, 7, 9].into_iter()),
1874 (vec![3, 5, 7, 9], 4, 1)
1875 );
1876 assert_eq!(
1877 antimodes(vec![1usize, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13].into_iter()),
1878 (vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 13, 1)
1879 );
1880 assert_eq!(
1881 antimodes(vec![1usize, 3, 3, 3].into_iter()),
1882 (vec![1], 1, 1)
1883 );
1884 assert_eq!(
1885 antimodes(vec![3usize, 3, 4, 4].into_iter()),
1886 (vec![3, 4], 2, 2)
1887 );
1888 assert_eq!(
1889 antimodes(
1890 vec![
1891 3usize, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13,
1892 14, 14, 15, 15
1893 ]
1894 .into_iter()
1895 ),
1896 (vec![3, 4, 5, 6, 7, 8, 9, 10, 11, 12], 13, 2)
1898 );
1899 assert_eq!(
1900 antimodes(
1901 vec![
1902 3usize, 3, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 4, 4, 5, 5, 6, 6, 7, 7, 13, 13,
1903 14, 14, 15, 15
1904 ]
1905 .into_iter()
1906 ),
1907 (vec![3, 4, 5, 6, 7, 8, 9, 10, 11, 12], 13, 2)
1908 );
1909 assert_eq!(
1910 antimodes(vec![3usize, 3, 3, 4].into_iter()),
1911 (vec![4], 1, 1)
1912 );
1913 assert_eq!(
1914 antimodes(vec![4usize, 3, 3, 3].into_iter()),
1915 (vec![4], 1, 1)
1916 );
1917 assert_eq!(
1918 antimodes(vec![1usize, 1, 2, 2].into_iter()),
1919 (vec![1, 2], 2, 2)
1920 );
1921 let vec: Vec<u32> = vec![];
1922 assert_eq!(antimodes(vec.into_iter()), (vec![], 0, 0));
1923 }
1924
1925 #[test]
1926 fn antimodes_floats() {
1927 assert_eq!(
1928 antimodes(vec![3_f64, 5.0, 7.0, 9.0].into_iter()),
1929 (vec![3.0, 5.0, 7.0, 9.0], 4, 1)
1930 );
1931 assert_eq!(
1932 antimodes(vec![3_f64, 3.0, 3.0, 3.0].into_iter()),
1933 (vec![], 0, 0)
1934 );
1935 assert_eq!(
1936 antimodes(vec![3_f64, 3.0, 4.0, 4.0].into_iter()),
1937 (vec![3.0, 4.0], 2, 2)
1938 );
1939 assert_eq!(
1940 antimodes(vec![1_f64, 1.0, 2.0, 3.0, 3.0].into_iter()),
1941 (vec![2.0], 1, 1)
1942 );
1943 }
1944
1945 #[test]
1946 fn test_custom_percentiles() {
1947 let mut unsorted: Unsorted<i32> = Unsorted::new();
1949 unsorted.extend(1..=11); let result = unsorted.custom_percentiles(&[25, 50, 75]).unwrap();
1952 assert_eq!(result, vec![3, 6, 9]);
1953
1954 let mut str_data = Unsorted::new();
1956 str_data.extend(vec!["a", "b", "c", "d", "e"]);
1957 let result = str_data.custom_percentiles(&[20, 40, 60, 80]).unwrap();
1958 assert_eq!(result, vec!["a", "b", "c", "d"]);
1959
1960 let mut char_data = Unsorted::new();
1962 char_data.extend('a'..='e');
1963 let result = char_data.custom_percentiles(&[25, 50, 75]).unwrap();
1964 assert_eq!(result, vec!['b', 'c', 'd']);
1965
1966 let mut float_data = Unsorted::new();
1968 float_data.extend(vec![1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9]);
1969 let result = float_data
1970 .custom_percentiles(&[10, 30, 50, 70, 90])
1971 .unwrap();
1972 assert_eq!(result, vec![1.1, 3.3, 5.5, 7.7, 9.9]);
1973
1974 let result = float_data.custom_percentiles(&[]).unwrap();
1976 assert_eq!(result, Vec::<f64>::new());
1977
1978 let result = float_data.custom_percentiles(&[50, 50, 50]).unwrap();
1980 assert_eq!(result, vec![5.5]);
1981
1982 let result = float_data.custom_percentiles(&[0, 100]).unwrap();
1984 assert_eq!(result, vec![1.1, 9.9]);
1985
1986 let result = float_data.custom_percentiles(&[75, 25, 50]).unwrap();
1988 assert_eq!(result, vec![3.3, 5.5, 7.7]); let mut single = Unsorted::new();
1992 single.add(42);
1993 let result = single.custom_percentiles(&[0, 50, 100]).unwrap();
1994 assert_eq!(result, vec![42, 42, 42]);
1995 }
1996
1997 #[test]
1998 fn quartiles_stream() {
1999 assert_eq!(
2000 quartiles(vec![3usize, 5, 7].into_iter()),
2001 Some((3., 5., 7.))
2002 );
2003 assert_eq!(
2004 quartiles(vec![3usize, 5, 7, 9].into_iter()),
2005 Some((4., 6., 8.))
2006 );
2007 assert_eq!(
2008 quartiles(vec![1usize, 2, 7, 11].into_iter()),
2009 Some((1.5, 4.5, 9.))
2010 );
2011 assert_eq!(
2012 quartiles(vec![3usize, 5, 7, 9, 12].into_iter()),
2013 Some((4., 7., 10.5))
2014 );
2015 assert_eq!(
2016 quartiles(vec![2usize, 2, 3, 8, 10].into_iter()),
2017 Some((2., 3., 9.))
2018 );
2019 assert_eq!(
2020 quartiles(vec![3usize, 5, 7, 9, 12, 20].into_iter()),
2021 Some((5., 8., 12.))
2022 );
2023 assert_eq!(
2024 quartiles(vec![0usize, 2, 4, 8, 10, 11].into_iter()),
2025 Some((2., 6., 10.))
2026 );
2027 assert_eq!(
2028 quartiles(vec![3usize, 5, 7, 9, 12, 20, 21].into_iter()),
2029 Some((5., 9., 20.))
2030 );
2031 assert_eq!(
2032 quartiles(vec![1usize, 5, 6, 6, 7, 10, 19].into_iter()),
2033 Some((5., 6., 10.))
2034 );
2035 }
2036
2037 #[test]
2038 fn quartiles_floats() {
2039 assert_eq!(
2040 quartiles(vec![3_f64, 5., 7.].into_iter()),
2041 Some((3., 5., 7.))
2042 );
2043 assert_eq!(
2044 quartiles(vec![3_f64, 5., 7., 9.].into_iter()),
2045 Some((4., 6., 8.))
2046 );
2047 assert_eq!(
2048 quartiles(vec![3_f64, 5., 7., 9., 12.].into_iter()),
2049 Some((4., 7., 10.5))
2050 );
2051 assert_eq!(
2052 quartiles(vec![3_f64, 5., 7., 9., 12., 20.].into_iter()),
2053 Some((5., 8., 12.))
2054 );
2055 assert_eq!(
2056 quartiles(vec![3_f64, 5., 7., 9., 12., 20., 21.].into_iter()),
2057 Some((5., 9., 20.))
2058 );
2059 }
2060
2061 #[test]
2062 fn test_quartiles_zero_copy_small() {
2063 let unsorted: Unsorted<i32> = Unsorted::new();
2065 assert_eq!(unsorted.quartiles_zero_copy(), None);
2066
2067 let mut unsorted = Unsorted::new();
2068 unsorted.extend(vec![1, 2]);
2069 assert_eq!(unsorted.quartiles_zero_copy(), None);
2070
2071 let mut unsorted = Unsorted::new();
2072 unsorted.extend(vec![1, 2, 3]);
2073 assert_eq!(unsorted.quartiles_zero_copy(), Some((1.0, 2.0, 3.0)));
2074
2075 let mut unsorted = Unsorted::new();
2077 unsorted.extend(vec![3, 5, 7, 9]);
2078 assert_eq!(unsorted.quartiles_zero_copy(), Some((4.0, 6.0, 8.0)));
2079 }
2080
2081 #[test]
2082 fn gini_empty() {
2083 let mut unsorted: Unsorted<i32> = Unsorted::new();
2084 assert_eq!(unsorted.gini(None), None);
2085 let empty_vec: Vec<i32> = vec![];
2086 assert_eq!(gini(empty_vec.into_iter(), None), None);
2087 }
2088
2089 #[test]
2090 fn gini_single_element() {
2091 let mut unsorted = Unsorted::new();
2092 unsorted.add(5);
2093 assert_eq!(unsorted.gini(None), Some(0.0));
2094 assert_eq!(gini(vec![5].into_iter(), None), Some(0.0));
2095 }
2096
2097 #[test]
2098 fn gini_perfect_equality() {
2099 let mut unsorted = Unsorted::new();
2101 unsorted.extend(vec![10, 10, 10, 10, 10]);
2102 let result = unsorted.gini(None).unwrap();
2103 assert!((result - 0.0).abs() < 1e-10, "Expected 0.0, got {}", result);
2104
2105 assert!((gini(vec![10, 10, 10, 10, 10].into_iter(), None).unwrap() - 0.0).abs() < 1e-10);
2106 }
2107
2108 #[test]
2109 fn gini_perfect_inequality() {
2110 let mut unsorted = Unsorted::new();
2113 unsorted.extend(vec![0, 0, 0, 0, 100]);
2114 let result = unsorted.gini(None).unwrap();
2115 assert!((result - 0.8).abs() < 1e-10, "Expected 0.8, got {}", result);
2118 }
2119
2120 #[test]
2121 fn gini_stream() {
2122 let result = gini(vec![1usize, 2, 3, 4, 5].into_iter(), None).unwrap();
2129 let expected = (2.0 * 55.0) / (5.0 * 15.0) - 6.0 / 5.0;
2130 assert!(
2131 (result - expected).abs() < 1e-10,
2132 "Expected {}, got {}",
2133 expected,
2134 result
2135 );
2136 }
2137
2138 #[test]
2139 fn gini_floats() {
2140 let mut unsorted = Unsorted::new();
2141 unsorted.extend(vec![1.0, 2.0, 3.0, 4.0, 5.0]);
2142 let result = unsorted.gini(None).unwrap();
2143 let expected = (2.0 * 55.0) / (5.0 * 15.0) - 6.0 / 5.0;
2144 assert!((result - expected).abs() < 1e-10);
2145
2146 assert!(
2147 (gini(vec![1.0f64, 2.0, 3.0, 4.0, 5.0].into_iter(), None).unwrap() - expected).abs()
2148 < 1e-10
2149 );
2150 }
2151
2152 #[test]
2153 fn gini_all_zeros() {
2154 let mut unsorted = Unsorted::new();
2156 unsorted.extend(vec![0, 0, 0, 0]);
2157 assert_eq!(unsorted.gini(None), None);
2158 assert_eq!(gini(vec![0, 0, 0, 0].into_iter(), None), None);
2159 }
2160
2161 #[test]
2162 fn gini_negative_values() {
2163 let mut unsorted = Unsorted::new();
2165 unsorted.extend(vec![-5, -3, -1, 1, 3, 5]);
2166 let result = unsorted.gini(None);
2167 assert_eq!(result, None);
2169
2170 let mut unsorted = Unsorted::new();
2172 unsorted.extend(vec![-2, -1, 0, 1, 2]);
2173 let result = unsorted.gini(None);
2174 assert_eq!(result, None);
2176
2177 let mut unsorted = Unsorted::new();
2180 unsorted.extend(vec![-1, 0, 1, 2, 3]);
2181 let result = unsorted.gini(None);
2182 assert_eq!(result, None);
2183 }
2184
2185 #[test]
2186 fn gini_known_cases() {
2187 let mut unsorted = Unsorted::new();
2189 unsorted.extend(vec![1, 1, 1, 1, 1]);
2190 let result = unsorted.gini(None).unwrap();
2191 assert!((result - 0.0).abs() < 1e-10);
2192
2193 let mut unsorted = Unsorted::new();
2195 unsorted.extend(vec![0, 0, 0, 0, 1]);
2196 let result = unsorted.gini(None).unwrap();
2197 assert!((result - 0.8).abs() < 1e-10);
2199
2200 let mut unsorted = Unsorted::new();
2202 unsorted.extend(vec![1, 2, 3]);
2203 let result = unsorted.gini(None).unwrap();
2204 let expected = (2.0 * 14.0) / (3.0 * 6.0) - 4.0 / 3.0;
2207 assert!((result - expected).abs() < 1e-10);
2208 }
2209
2210 #[test]
2211 fn gini_precalc_sum() {
2212 let mut unsorted = Unsorted::new();
2214 unsorted.extend(vec![1, 2, 3, 4, 5]);
2215 let precalc_sum = Some(15.0);
2216 let result = unsorted.gini(precalc_sum).unwrap();
2217 let expected = (2.0 * 55.0) / (5.0 * 15.0) - 6.0 / 5.0;
2218 assert!((result - expected).abs() < 1e-10);
2219
2220 let mut unsorted2 = Unsorted::new();
2222 unsorted2.extend(vec![1, 2, 3, 4, 5]);
2223 let result2 = unsorted2.gini(None).unwrap();
2224 assert!((result - result2).abs() < 1e-10);
2225 }
2226
2227 #[test]
2228 fn gini_large_dataset() {
2229 let data: Vec<i32> = (1..=1000).collect();
2231 let result = gini(data.iter().copied(), None);
2232 assert!(result.is_some());
2233 let gini_val = result.unwrap();
2234 assert!(gini_val > 0.0 && gini_val < 0.5);
2236 }
2237
2238 #[test]
2239 fn gini_unsorted_vs_sorted() {
2240 let mut unsorted1 = Unsorted::new();
2242 unsorted1.extend(vec![5, 2, 8, 1, 9, 3, 7, 4, 6]);
2243 let result1 = unsorted1.gini(None).unwrap();
2244
2245 let mut unsorted2 = Unsorted::new();
2246 unsorted2.extend(vec![1, 2, 3, 4, 5, 6, 7, 8, 9]);
2247 let result2 = unsorted2.gini(None).unwrap();
2248
2249 assert!((result1 - result2).abs() < 1e-10);
2250 }
2251
2252 #[test]
2253 fn gini_small_values() {
2254 let mut unsorted = Unsorted::new();
2256 unsorted.extend(vec![0.001, 0.002, 0.003, 0.004, 0.005]);
2257 let result = unsorted.gini(None);
2258 assert!(result.is_some());
2259 let expected = (2.0 * 55.0) / (5.0 * 15.0) - 6.0 / 5.0;
2261 assert!((result.unwrap() - expected).abs() < 1e-10);
2262 }
2263
2264 #[test]
2265 fn gini_large_values() {
2266 let mut unsorted = Unsorted::new();
2268 unsorted.extend(vec![1000, 2000, 3000, 4000, 5000]);
2269 let result = unsorted.gini(None);
2270 assert!(result.is_some());
2271 let expected = (2.0 * 55.0) / (5.0 * 15.0) - 6.0 / 5.0;
2273 assert!((result.unwrap() - expected).abs() < 1e-10);
2274 }
2275
2276 #[test]
2277 fn gini_two_elements() {
2278 let mut unsorted = Unsorted::new();
2280 unsorted.extend(vec![1, 2]);
2281 let result = unsorted.gini(None).unwrap();
2282 let expected = (2.0 * 5.0) / (2.0 * 3.0) - 3.0 / 2.0;
2285 assert!((result - expected).abs() < 1e-10);
2286 }
2287
2288 #[test]
2289 fn gini_precalc_sum_zero() {
2290 let mut unsorted = Unsorted::new();
2292 unsorted.extend(vec![1, 2, 3, 4, 5]);
2293 let result = unsorted.gini(Some(0.0));
2294 assert_eq!(result, None);
2295 }
2296
2297 #[test]
2298 fn gini_precalc_sum_negative() {
2299 let mut unsorted = Unsorted::new();
2301 unsorted.extend(vec![-5, -3, -1, 1, 3]);
2302 let result = unsorted.gini(None);
2303 assert_eq!(result, None);
2304
2305 let mut unsorted = Unsorted::new();
2307 unsorted.extend(vec![1, 2, 3]);
2308 let result = unsorted.gini(Some(-5.0));
2309 assert_eq!(result, None);
2310 }
2311
2312 #[test]
2313 fn gini_different_types() {
2314 let mut unsorted_u32 = Unsorted::new();
2316 unsorted_u32.extend(vec![1u32, 2, 3, 4, 5]);
2317 let result_u32 = unsorted_u32.gini(None).unwrap();
2318
2319 let mut unsorted_i64 = Unsorted::new();
2320 unsorted_i64.extend(vec![1i64, 2, 3, 4, 5]);
2321 let result_i64 = unsorted_i64.gini(None).unwrap();
2322
2323 let expected = (2.0 * 55.0) / (5.0 * 15.0) - 6.0 / 5.0;
2324 assert!((result_u32 - expected).abs() < 1e-10);
2325 assert!((result_i64 - expected).abs() < 1e-10);
2326 }
2327
2328 #[test]
2329 fn gini_extreme_inequality() {
2330 let mut unsorted = Unsorted::new();
2332 unsorted.extend(vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 1000]);
2333 let result = unsorted.gini(None).unwrap();
2334 assert!((result - 0.9).abs() < 1e-10);
2337 }
2338
2339 #[test]
2340 fn gini_duplicate_values() {
2341 let mut unsorted = Unsorted::new();
2343 unsorted.extend(vec![1, 1, 1, 5, 5, 5, 10, 10, 10]);
2344 let result = unsorted.gini(None);
2345 assert!(result.is_some());
2346 let gini_val = result.unwrap();
2348 assert!(gini_val >= 0.0 && gini_val <= 1.0);
2349 }
2350
2351 #[test]
2352 fn kurtosis_empty() {
2353 let mut unsorted: Unsorted<i32> = Unsorted::new();
2354 assert_eq!(unsorted.kurtosis(None, None), None);
2355 let empty_vec: Vec<i32> = vec![];
2356 assert_eq!(kurtosis(empty_vec.into_iter(), None, None), None);
2357 }
2358
2359 #[test]
2360 fn kurtosis_small() {
2361 let mut unsorted = Unsorted::new();
2363 unsorted.extend(vec![1, 2]);
2364 assert_eq!(unsorted.kurtosis(None, None), None);
2365
2366 let mut unsorted = Unsorted::new();
2367 unsorted.extend(vec![1, 2, 3]);
2368 assert_eq!(unsorted.kurtosis(None, None), None);
2369 }
2370
2371 #[test]
2372 fn kurtosis_normal_distribution() {
2373 let mut unsorted = Unsorted::new();
2375 unsorted.extend(vec![1, 2, 3, 4, 5]);
2376 let result = unsorted.kurtosis(None, None);
2377 assert!(result.is_some());
2378 }
2380
2381 #[test]
2382 fn kurtosis_all_same() {
2383 let mut unsorted = Unsorted::new();
2385 unsorted.extend(vec![5, 5, 5, 5]);
2386 assert_eq!(unsorted.kurtosis(None, None), None);
2387 }
2388
2389 #[test]
2390 fn kurtosis_stream() {
2391 let result = kurtosis(vec![1usize, 2, 3, 4, 5].into_iter(), None, None);
2392 assert!(result.is_some());
2393 }
2394
2395 #[test]
2396 fn kurtosis_precalc_mean_variance() {
2397 let mut unsorted = Unsorted::new();
2399 unsorted.extend(vec![1, 2, 3, 4, 5]);
2400
2401 let mean = 3.0f64;
2403 let variance = ((1.0f64 - 3.0).powi(2)
2404 + (2.0f64 - 3.0).powi(2)
2405 + (3.0f64 - 3.0).powi(2)
2406 + (4.0f64 - 3.0).powi(2)
2407 + (5.0f64 - 3.0).powi(2))
2408 / 5.0;
2409
2410 let result = unsorted.kurtosis(Some(mean), Some(variance));
2411 assert!(result.is_some());
2412
2413 let mut unsorted2 = Unsorted::new();
2415 unsorted2.extend(vec![1, 2, 3, 4, 5]);
2416 let result2 = unsorted2.kurtosis(None, None);
2417 assert!((result.unwrap() - result2.unwrap()).abs() < 1e-10);
2418 }
2419
2420 #[test]
2421 fn kurtosis_precalc_mean_only() {
2422 let mut unsorted = Unsorted::new();
2424 unsorted.extend(vec![1, 2, 3, 4, 5]);
2425 let mean = 3.0f64;
2426
2427 let result = unsorted.kurtosis(Some(mean), None);
2428 assert!(result.is_some());
2429
2430 let mut unsorted2 = Unsorted::new();
2432 unsorted2.extend(vec![1, 2, 3, 4, 5]);
2433 let result2 = unsorted2.kurtosis(None, None);
2434 assert!((result.unwrap() - result2.unwrap()).abs() < 1e-10);
2435 }
2436
2437 #[test]
2438 fn kurtosis_precalc_variance_only() {
2439 let mut unsorted = Unsorted::new();
2441 unsorted.extend(vec![1, 2, 3, 4, 5]);
2442 let variance = ((1.0f64 - 3.0).powi(2)
2443 + (2.0f64 - 3.0).powi(2)
2444 + (3.0f64 - 3.0).powi(2)
2445 + (4.0f64 - 3.0).powi(2)
2446 + (5.0f64 - 3.0).powi(2))
2447 / 5.0;
2448
2449 let result = unsorted.kurtosis(None, Some(variance));
2450 assert!(result.is_some());
2451
2452 let mut unsorted2 = Unsorted::new();
2454 unsorted2.extend(vec![1, 2, 3, 4, 5]);
2455 let result2 = unsorted2.kurtosis(None, None);
2456 assert!((result.unwrap() - result2.unwrap()).abs() < 1e-10);
2457 }
2458
2459 #[test]
2460 fn kurtosis_exact_calculation() {
2461 let mut unsorted = Unsorted::new();
2469 unsorted.extend(vec![1, 2, 3, 4]);
2470 let result = unsorted.kurtosis(None, None).unwrap();
2471 assert!(result.is_finite());
2473 }
2474
2475 #[test]
2476 fn kurtosis_uniform_distribution() {
2477 let mut unsorted = Unsorted::new();
2479 unsorted.extend(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
2480 let result = unsorted.kurtosis(None, None).unwrap();
2481 assert!(result.is_finite());
2484 }
2485
2486 #[test]
2487 fn kurtosis_large_dataset() {
2488 let data: Vec<i32> = (1..=1000).collect();
2490 let result = kurtosis(data.iter().copied(), None, None);
2491 assert!(result.is_some());
2492 let kurt_val = result.unwrap();
2493 assert!(kurt_val.is_finite());
2494 }
2495
2496 #[test]
2497 fn kurtosis_unsorted_vs_sorted() {
2498 let mut unsorted1 = Unsorted::new();
2500 unsorted1.extend(vec![5, 2, 8, 1, 9, 3, 7, 4, 6]);
2501 let result1 = unsorted1.kurtosis(None, None).unwrap();
2502
2503 let mut unsorted2 = Unsorted::new();
2504 unsorted2.extend(vec![1, 2, 3, 4, 5, 6, 7, 8, 9]);
2505 let result2 = unsorted2.kurtosis(None, None).unwrap();
2506
2507 assert!((result1 - result2).abs() < 1e-10);
2508 }
2509
2510 #[test]
2511 fn kurtosis_minimum_size() {
2512 let mut unsorted = Unsorted::new();
2514 unsorted.extend(vec![1, 2, 3, 4]);
2515 let result = unsorted.kurtosis(None, None);
2516 assert!(result.is_some());
2517 assert!(result.unwrap().is_finite());
2518 }
2519
2520 #[test]
2521 fn kurtosis_heavy_tailed() {
2522 let mut unsorted = Unsorted::new();
2524 unsorted.extend(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 100]);
2525 let result = unsorted.kurtosis(None, None).unwrap();
2526 assert!(result.is_finite());
2528 assert!(result > -10.0); }
2531
2532 #[test]
2533 fn kurtosis_light_tailed() {
2534 let mut unsorted = Unsorted::new();
2536 unsorted.extend(vec![10, 11, 12, 13, 14, 15, 16, 17, 18, 19]);
2537 let result = unsorted.kurtosis(None, None).unwrap();
2538 assert!(result.is_finite());
2540 }
2541
2542 #[test]
2543 fn kurtosis_small_variance() {
2544 let mut unsorted = Unsorted::new();
2546 unsorted.extend(vec![10.0, 10.001, 10.002, 10.003, 10.004]);
2547 let result = unsorted.kurtosis(None, None);
2548 assert!(result.is_some());
2550 assert!(result.unwrap().is_finite());
2551 }
2552
2553 #[test]
2554 fn kurtosis_precalc_zero_variance() {
2555 let mut unsorted = Unsorted::new();
2557 unsorted.extend(vec![1, 2, 3, 4, 5]);
2558 let result = unsorted.kurtosis(None, Some(0.0));
2559 assert_eq!(result, None);
2560 }
2561
2562 #[test]
2563 fn kurtosis_precalc_negative_variance() {
2564 let mut unsorted = Unsorted::new();
2566 unsorted.extend(vec![1, 2, 3, 4, 5]);
2567 let result = unsorted.kurtosis(None, Some(-1.0));
2569 let _ = result;
2574 }
2575
2576 #[test]
2577 fn kurtosis_different_types() {
2578 let mut unsorted_u32 = Unsorted::new();
2580 unsorted_u32.extend(vec![1u32, 2, 3, 4, 5]);
2581 let result_u32 = unsorted_u32.kurtosis(None, None).unwrap();
2582
2583 let mut unsorted_i64 = Unsorted::new();
2584 unsorted_i64.extend(vec![1i64, 2, 3, 4, 5]);
2585 let result_i64 = unsorted_i64.kurtosis(None, None).unwrap();
2586
2587 assert!((result_u32 - result_i64).abs() < 1e-10);
2588 }
2589
2590 #[test]
2591 fn kurtosis_floating_point_precision() {
2592 let mut unsorted = Unsorted::new();
2594 unsorted.extend(vec![1.1, 2.2, 3.3, 4.4, 5.5]);
2595 let result = unsorted.kurtosis(None, None);
2596 assert!(result.is_some());
2597 assert!(result.unwrap().is_finite());
2598 }
2599
2600 #[test]
2601 fn kurtosis_negative_values() {
2602 let mut unsorted = Unsorted::new();
2604 unsorted.extend(vec![-5, -3, -1, 1, 3, 5]);
2605 let result = unsorted.kurtosis(None, None);
2606 assert!(result.is_some());
2607 assert!(result.unwrap().is_finite());
2608 }
2609
2610 #[test]
2611 fn kurtosis_mixed_positive_negative() {
2612 let mut unsorted = Unsorted::new();
2614 unsorted.extend(vec![-10, -5, 0, 5, 10]);
2615 let result = unsorted.kurtosis(None, None);
2616 assert!(result.is_some());
2617 assert!(result.unwrap().is_finite());
2618 }
2619
2620 #[test]
2621 fn kurtosis_duplicate_values() {
2622 let mut unsorted = Unsorted::new();
2624 unsorted.extend(vec![1, 1, 2, 2, 3, 3, 4, 4, 5, 5]);
2625 let result = unsorted.kurtosis(None, None);
2626 assert!(result.is_some());
2627 assert!(result.unwrap().is_finite());
2628 }
2629
2630 #[test]
2631 fn kurtosis_precalc_mean_wrong() {
2632 let mut unsorted1 = Unsorted::new();
2634 unsorted1.extend(vec![1, 2, 3, 4, 5]);
2635 let correct_result = unsorted1.kurtosis(None, None).unwrap();
2636
2637 let mut unsorted2 = Unsorted::new();
2638 unsorted2.extend(vec![1, 2, 3, 4, 5]);
2639 let wrong_mean = 10.0; let wrong_result = unsorted2.kurtosis(Some(wrong_mean), None).unwrap();
2641
2642 assert!((correct_result - wrong_result).abs() > 1e-5);
2644 }
2645
2646 #[test]
2647 fn percentile_rank_empty() {
2648 let mut unsorted: Unsorted<i32> = Unsorted::new();
2649 assert_eq!(unsorted.percentile_rank(5), None);
2650 let empty_vec: Vec<i32> = vec![];
2651 assert_eq!(percentile_rank(empty_vec.into_iter(), 5), None);
2652 }
2653
2654 #[test]
2655 fn percentile_rank_basic() {
2656 let mut unsorted = Unsorted::new();
2657 unsorted.extend(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
2658
2659 assert_eq!(unsorted.percentile_rank(0), Some(0.0));
2661
2662 assert_eq!(unsorted.percentile_rank(11), Some(100.0));
2664
2665 let rank = unsorted.percentile_rank(5).unwrap();
2667 assert!((rank - 50.0).abs() < 1.0);
2668
2669 let rank = unsorted.percentile_rank(1).unwrap();
2671 assert!((rank - 10.0).abs() < 1.0);
2672 }
2673
2674 #[test]
2675 fn percentile_rank_duplicates() {
2676 let mut unsorted = Unsorted::new();
2677 unsorted.extend(vec![1, 1, 2, 2, 3, 3, 4, 4, 5, 5]);
2678
2679 let rank = unsorted.percentile_rank(2).unwrap();
2681 assert!((rank - 40.0).abs() < 1.0);
2682 }
2683
2684 #[test]
2685 fn percentile_rank_stream() {
2686 let result = percentile_rank(vec![1usize, 2, 3, 4, 5].into_iter(), 3);
2687 assert_eq!(result, Some(60.0)); }
2689
2690 #[test]
2691 fn atkinson_empty() {
2692 let mut unsorted: Unsorted<i32> = Unsorted::new();
2693 assert_eq!(unsorted.atkinson(1.0, None, None), None);
2694 let empty_vec: Vec<i32> = vec![];
2695 assert_eq!(atkinson(empty_vec.into_iter(), 1.0, None, None), None);
2696 }
2697
2698 #[test]
2699 fn atkinson_single_element() {
2700 let mut unsorted = Unsorted::new();
2701 unsorted.add(5);
2702 assert_eq!(unsorted.atkinson(1.0, None, None), Some(0.0));
2703 assert_eq!(atkinson(vec![5].into_iter(), 1.0, None, None), Some(0.0));
2704 }
2705
2706 #[test]
2707 fn atkinson_perfect_equality() {
2708 let mut unsorted = Unsorted::new();
2710 unsorted.extend(vec![10, 10, 10, 10, 10]);
2711 let result = unsorted.atkinson(1.0, None, None).unwrap();
2712 assert!((result - 0.0).abs() < 1e-10);
2713 }
2714
2715 #[test]
2716 fn atkinson_epsilon_zero() {
2717 let mut unsorted = Unsorted::new();
2719 unsorted.extend(vec![1, 2, 3, 4, 5]);
2720 let result = unsorted.atkinson(0.0, None, None).unwrap();
2721 assert!((result - 0.0).abs() < 1e-10);
2722 }
2723
2724 #[test]
2725 fn atkinson_epsilon_one() {
2726 let mut unsorted = Unsorted::new();
2728 unsorted.extend(vec![1, 2, 3, 4, 5]);
2729 let result = unsorted.atkinson(1.0, None, None);
2730 assert!(result.is_some());
2731 }
2732
2733 #[test]
2734 fn atkinson_negative_epsilon() {
2735 let mut unsorted = Unsorted::new();
2736 unsorted.extend(vec![1, 2, 3, 4, 5]);
2737 assert_eq!(unsorted.atkinson(-1.0, None, None), None);
2738 }
2739
2740 #[test]
2741 fn atkinson_zero_mean() {
2742 let mut unsorted = Unsorted::new();
2744 unsorted.extend(vec![0, 0, 0, 0]);
2745 assert_eq!(unsorted.atkinson(1.0, None, None), None);
2746 }
2747
2748 #[test]
2749 fn atkinson_stream() {
2750 let result = atkinson(vec![1usize, 2, 3, 4, 5].into_iter(), 1.0, None, None);
2751 assert!(result.is_some());
2752 }
2753
2754 #[test]
2755 fn atkinson_precalc_mean_geometric_sum() {
2756 let mut unsorted = Unsorted::new();
2758 unsorted.extend(vec![1, 2, 3, 4, 5]);
2759
2760 let mean = 3.0f64;
2762 let geometric_sum = 1.0f64.ln() + 2.0f64.ln() + 3.0f64.ln() + 4.0f64.ln() + 5.0f64.ln();
2763
2764 let result = unsorted.atkinson(1.0, Some(mean), Some(geometric_sum));
2765 assert!(result.is_some());
2766
2767 let mut unsorted2 = Unsorted::new();
2769 unsorted2.extend(vec![1, 2, 3, 4, 5]);
2770 let result2 = unsorted2.atkinson(1.0, None, None);
2771 assert!((result.unwrap() - result2.unwrap()).abs() < 1e-10);
2772 }
2773
2774 #[test]
2775 fn atkinson_precalc_mean_only() {
2776 let mut unsorted = Unsorted::new();
2778 unsorted.extend(vec![1, 2, 3, 4, 5]);
2779 let mean = 3.0f64;
2780
2781 let result = unsorted.atkinson(1.0, Some(mean), None);
2782 assert!(result.is_some());
2783
2784 let mut unsorted2 = Unsorted::new();
2786 unsorted2.extend(vec![1, 2, 3, 4, 5]);
2787 let result2 = unsorted2.atkinson(1.0, None, None);
2788 assert!((result.unwrap() - result2.unwrap()).abs() < 1e-10);
2789 }
2790
2791 #[test]
2792 fn atkinson_precalc_geometric_sum_only() {
2793 let mut unsorted = Unsorted::new();
2795 unsorted.extend(vec![1, 2, 3, 4, 5]);
2796 let geometric_sum = 1.0f64.ln() + 2.0f64.ln() + 3.0f64.ln() + 4.0f64.ln() + 5.0f64.ln();
2797
2798 let result = unsorted.atkinson(1.0, None, Some(geometric_sum));
2799 assert!(result.is_some());
2800
2801 let mut unsorted2 = Unsorted::new();
2803 unsorted2.extend(vec![1, 2, 3, 4, 5]);
2804 let result2 = unsorted2.atkinson(1.0, None, None);
2805 assert!((result.unwrap() - result2.unwrap()).abs() < 1e-10);
2806 }
2807}
2808
2809#[cfg(test)]
2810mod bench {
2811 use super::*;
2812 use std::time::Instant;
2813
2814 #[test]
2815 #[ignore] fn comprehensive_quartiles_benchmark() {
2817 let data_sizes = vec![
2819 1_000, 10_000, 100_000, 500_000, 1_000_000, 2_000_000, 5_000_000, 10_000_000,
2820 ];
2821
2822 println!("=== COMPREHENSIVE QUARTILES BENCHMARK ===\n");
2823
2824 for size in data_sizes {
2825 println!("--- Testing with {} elements ---", size);
2826
2827 let test_patterns = vec![
2829 ("Random", generate_random_data(size)),
2830 ("Reverse Sorted", {
2831 let mut v = Vec::with_capacity(size);
2832 for x in (0..size).rev() {
2833 v.push(x as i32);
2834 }
2835 v
2836 }),
2837 ("Already Sorted", {
2838 let mut v = Vec::with_capacity(size);
2839 for x in 0..size {
2840 v.push(x as i32);
2841 }
2842 v
2843 }),
2844 ("Many Duplicates", {
2845 let mut v = Vec::with_capacity(size);
2847 let chunk_size = size / 100;
2848 for i in 0..100 {
2849 v.extend(std::iter::repeat_n(i, chunk_size));
2850 }
2851 v.extend(std::iter::repeat_n(0, size - v.len()));
2853 v
2854 }),
2855 ];
2856
2857 for (pattern_name, test_data) in test_patterns {
2858 println!("\n Pattern: {}", pattern_name);
2859
2860 let mut unsorted1 = Unsorted::new();
2862 unsorted1.extend(test_data.clone());
2863
2864 let start = Instant::now();
2865 let result_sorted = unsorted1.quartiles();
2866 let sorted_time = start.elapsed();
2867
2868 let mut unsorted2 = Unsorted::new();
2870 unsorted2.extend(test_data.clone());
2871
2872 let start = Instant::now();
2873 let result_selection = unsorted2.quartiles_with_selection();
2874 let selection_time = start.elapsed();
2875
2876 let mut unsorted3 = Unsorted::new();
2878 unsorted3.extend(test_data);
2879
2880 let start = Instant::now();
2881 let result_zero_copy = unsorted3.quartiles_zero_copy();
2882 let zero_copy_time = start.elapsed();
2883
2884 assert_eq!(result_sorted, result_selection);
2886 assert_eq!(result_sorted, result_zero_copy);
2887
2888 let selection_speedup =
2889 sorted_time.as_nanos() as f64 / selection_time.as_nanos() as f64;
2890 let zero_copy_speedup =
2891 sorted_time.as_nanos() as f64 / zero_copy_time.as_nanos() as f64;
2892
2893 println!(" Sorting: {:>12?}", sorted_time);
2894 println!(
2895 " Selection: {:>12?} (speedup: {:.2}x)",
2896 selection_time, selection_speedup
2897 );
2898 println!(
2899 " Zero-copy: {:>12?} (speedup: {:.2}x)",
2900 zero_copy_time, zero_copy_speedup
2901 );
2902
2903 let best_algorithm =
2904 if zero_copy_speedup > 1.0 && zero_copy_speedup >= selection_speedup {
2905 "ZERO-COPY"
2906 } else if selection_speedup > 1.0 {
2907 "SELECTION"
2908 } else {
2909 "SORTING"
2910 };
2911 println!(" Best: {}", best_algorithm);
2912 }
2913
2914 println!(); }
2916 }
2917
2918 fn generate_random_data(size: usize) -> Vec<i32> {
2920 let mut rng = 1234567u64;
2922 let mut vec = Vec::with_capacity(size);
2923 for _ in 0..size {
2924 rng = rng.wrapping_mul(1103515245).wrapping_add(12345);
2925 vec.push((rng >> 16) as i32);
2926 }
2927 vec
2928 }
2929
2930 #[test]
2931 #[ignore] fn find_selection_threshold() {
2933 println!("=== FINDING SELECTION ALGORITHM THRESHOLD ===\n");
2934
2935 let mut found_threshold = None;
2937 let test_sizes = vec![
2938 1_000_000, 2_000_000, 3_000_000, 4_000_000, 5_000_000, 7_500_000, 10_000_000,
2939 15_000_000, 20_000_000, 25_000_000, 30_000_000,
2940 ];
2941
2942 for size in test_sizes {
2943 println!("Testing size: {}", size);
2944
2945 let test_data = generate_random_data(size);
2947
2948 let iterations = 3;
2950 let mut sorting_total = 0u128;
2951 let mut selection_total = 0u128;
2952 let mut zero_copy_total = 0u128;
2953
2954 for i in 0..iterations {
2955 println!(" Iteration {}/{}", i + 1, iterations);
2956
2957 let mut unsorted1 = Unsorted::new();
2959 unsorted1.extend(test_data.clone());
2960
2961 let start = Instant::now();
2962 let _result_sorted = unsorted1.quartiles();
2963 sorting_total += start.elapsed().as_nanos();
2964
2965 let mut unsorted2 = Unsorted::new();
2967 unsorted2.extend(test_data.clone());
2968
2969 let start = Instant::now();
2970 let _result_selection = unsorted2.quartiles_with_selection();
2971 selection_total += start.elapsed().as_nanos();
2972
2973 let mut unsorted3 = Unsorted::new();
2975 unsorted3.extend(test_data.clone());
2976
2977 let start = Instant::now();
2978 let _result_zero_copy = unsorted3.quartiles_zero_copy();
2979 zero_copy_total += start.elapsed().as_nanos();
2980 }
2981
2982 let avg_sorting = sorting_total / iterations as u128;
2983 let avg_selection = selection_total / iterations as u128;
2984 let avg_zero_copy = zero_copy_total / iterations as u128;
2985 let selection_speedup = avg_sorting as f64 / avg_selection as f64;
2986 let zero_copy_speedup = avg_sorting as f64 / avg_zero_copy as f64;
2987
2988 println!(
2989 " Average sorting: {:>12.2}ms",
2990 avg_sorting as f64 / 1_000_000.0
2991 );
2992 println!(
2993 " Average selection: {:>12.2}ms (speedup: {:.2}x)",
2994 avg_selection as f64 / 1_000_000.0,
2995 selection_speedup
2996 );
2997 println!(
2998 " Average zero-copy: {:>12.2}ms (speedup: {:.2}x)",
2999 avg_zero_copy as f64 / 1_000_000.0,
3000 zero_copy_speedup
3001 );
3002
3003 if (selection_speedup > 1.0 || zero_copy_speedup > 1.0) && found_threshold.is_none() {
3004 found_threshold = Some(size);
3005 let best_method = if zero_copy_speedup > selection_speedup {
3006 "Zero-copy"
3007 } else {
3008 "Selection"
3009 };
3010 println!(
3011 " *** THRESHOLD FOUND: {} becomes faster at {} elements ***",
3012 best_method, size
3013 );
3014 }
3015
3016 println!();
3017 }
3018
3019 match found_threshold {
3020 Some(threshold) => println!(
3021 "🎯 Selection algorithm becomes faster at approximately {} elements",
3022 threshold
3023 ),
3024 None => println!("❌ Selection algorithm did not become faster in the tested range"),
3025 }
3026 }
3027
3028 #[test]
3029 #[ignore] fn benchmark_different_data_types() {
3031 println!("=== BENCHMARKING DIFFERENT DATA TYPES ===\n");
3032
3033 let size = 5_000_000; println!("Testing with f64 data:");
3037 let float_data: Vec<f64> = generate_random_data(size)
3038 .into_iter()
3039 .map(|x| x as f64 / 1000.0)
3040 .collect();
3041
3042 let mut unsorted1 = Unsorted::new();
3043 unsorted1.extend(float_data.clone());
3044 let start = Instant::now();
3045 let _result = unsorted1.quartiles();
3046 let sorting_time = start.elapsed();
3047
3048 let mut unsorted2 = Unsorted::new();
3049 unsorted2.extend(float_data.clone());
3050 let start = Instant::now();
3051 let _result = unsorted2.quartiles_with_selection();
3052 let selection_time = start.elapsed();
3053
3054 let mut unsorted3 = Unsorted::new();
3055 unsorted3.extend(float_data);
3056 let start = Instant::now();
3057 let _result = unsorted3.quartiles_zero_copy();
3058 let zero_copy_time = start.elapsed();
3059
3060 println!(" Sorting: {:?}", sorting_time);
3061 println!(" Selection: {:?}", selection_time);
3062 println!(" Zero-copy: {:?}", zero_copy_time);
3063 println!(
3064 " Selection Speedup: {:.2}x",
3065 sorting_time.as_nanos() as f64 / selection_time.as_nanos() as f64
3066 );
3067 println!(
3068 " Zero-copy Speedup: {:.2}x\n",
3069 sorting_time.as_nanos() as f64 / zero_copy_time.as_nanos() as f64
3070 );
3071
3072 println!("Testing with i64 data:");
3074 let int64_data: Vec<i64> = generate_random_data(size)
3075 .into_iter()
3076 .map(|x| x as i64 * 1000)
3077 .collect();
3078
3079 let mut unsorted1 = Unsorted::new();
3080 unsorted1.extend(int64_data.clone());
3081 let start = Instant::now();
3082 let _result = unsorted1.quartiles();
3083 let sorting_time = start.elapsed();
3084
3085 let mut unsorted2 = Unsorted::new();
3086 unsorted2.extend(int64_data.clone());
3087 let start = Instant::now();
3088 let _result = unsorted2.quartiles_with_selection();
3089 let selection_time = start.elapsed();
3090
3091 let mut unsorted3 = Unsorted::new();
3092 unsorted3.extend(int64_data);
3093 let start = Instant::now();
3094 let _result = unsorted3.quartiles_zero_copy();
3095 let zero_copy_time = start.elapsed();
3096
3097 println!(" Sorting: {:?}", sorting_time);
3098 println!(" Selection: {:?}", selection_time);
3099 println!(" Zero-copy: {:?}", zero_copy_time);
3100 println!(
3101 " Selection Speedup: {:.2}x",
3102 sorting_time.as_nanos() as f64 / selection_time.as_nanos() as f64
3103 );
3104 println!(
3105 " Zero-copy Speedup: {:.2}x",
3106 sorting_time.as_nanos() as f64 / zero_copy_time.as_nanos() as f64
3107 );
3108 }
3109}